) => {
+ await navigate(`./edit/${thesaurus.original._id}`);
};
return (
diff --git a/app/react/V2/Routes/Settings/Thesauri/components/ThesaurusActions.tsx b/app/react/V2/Routes/Settings/Thesauri/components/ThesaurusActions.tsx
index 352c618f3e..f18a032245 100644
--- a/app/react/V2/Routes/Settings/Thesauri/components/ThesaurusActions.tsx
+++ b/app/react/V2/Routes/Settings/Thesauri/components/ThesaurusActions.tsx
@@ -1,6 +1,6 @@
/* eslint-disable react/no-multi-comp */
import React, { Dispatch, SetStateAction } from 'react';
-import { Link } from 'react-router-dom';
+import { Link } from 'react-router';
import { Translate } from 'app/I18N';
import { Button, ConfirmationModal } from 'app/V2/Components/UI';
import { ConfirmationCallback } from '../helpers';
diff --git a/app/react/V2/Routes/Settings/Thesauri/helpers.ts b/app/react/V2/Routes/Settings/Thesauri/helpers.ts
index 0b62859fa3..a005f5fdf6 100644
--- a/app/react/V2/Routes/Settings/Thesauri/helpers.ts
+++ b/app/react/V2/Routes/Settings/Thesauri/helpers.ts
@@ -1,5 +1,5 @@
import { SetStateAction } from 'react';
-import { LoaderFunction } from 'react-router-dom';
+import { LoaderFunction } from 'react-router';
import { IncomingHttpHeaders } from 'http';
import { Row, RowSelectionState } from '@tanstack/react-table';
import { assign, isEqual, orderBy, remove } from 'lodash';
diff --git a/app/react/V2/Routes/Settings/Thesauri/specs/Thesauri.spec.tsx b/app/react/V2/Routes/Settings/Thesauri/specs/Thesauri.spec.tsx
index 571d27efaa..3b9c701f5f 100644
--- a/app/react/V2/Routes/Settings/Thesauri/specs/Thesauri.spec.tsx
+++ b/app/react/V2/Routes/Settings/Thesauri/specs/Thesauri.spec.tsx
@@ -13,7 +13,7 @@ import {
render,
cleanup,
} from '@testing-library/react/pure';
-import { createMemoryRouter, RouterProvider } from 'react-router-dom';
+import { createMemoryRouter, RouterProvider } from 'react-router';
import { has } from 'lodash';
import { templatesAtom } from 'V2/atoms';
import { TestAtomStoreProvider } from 'V2/testing';
diff --git a/app/react/V2/Routes/Settings/Thesauri/specs/__snapshots__/Thesauri.spec.tsx.snap b/app/react/V2/Routes/Settings/Thesauri/specs/__snapshots__/Thesauri.spec.tsx.snap
index 32324de5e0..f4b305451f 100644
--- a/app/react/V2/Routes/Settings/Thesauri/specs/__snapshots__/Thesauri.spec.tsx.snap
+++ b/app/react/V2/Routes/Settings/Thesauri/specs/__snapshots__/Thesauri.spec.tsx.snap
@@ -664,23 +664,6 @@ exports[`Settings Thesauri ThesauriList render existing thesauri should show a l
-
-
- To pick up a draggable item, press the space bar.
- While dragging, use the arrow keys to move the item.
- Press space again to drop the item in its new position, or press escape to cancel.
-
-
-
{
const values = prepareValuesToSave(data.formValues, translations);
formData.set('intent', 'form-submit');
formData.set('data', JSON.stringify(values));
- fetcher.submit(formData, { method: 'post' });
+ await fetcher.submit(formData, { method: 'post' });
reset({}, { keepValues: true });
};
@@ -268,7 +268,7 @@ const EditTranslations = () => {
const formData = new FormData();
formData.set('intent', 'file-upload');
formData.set('data', file);
- fetcher.submit(formData, { method: 'post', encType: 'multipart/form-data' });
+ await fetcher.submit(formData, { method: 'post', encType: 'multipart/form-data' });
reset({}, { keepValues: true });
}
};
diff --git a/app/react/V2/Routes/Settings/Translations/TranslationsList.tsx b/app/react/V2/Routes/Settings/Translations/TranslationsList.tsx
index c4a450c4d5..d3f477a85a 100644
--- a/app/react/V2/Routes/Settings/Translations/TranslationsList.tsx
+++ b/app/react/V2/Routes/Settings/Translations/TranslationsList.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { IncomingHttpHeaders } from 'http';
-import { useLoaderData, LoaderFunction } from 'react-router-dom';
+import { useLoaderData, LoaderFunction } from 'react-router';
import { createColumnHelper } from '@tanstack/react-table';
import { Translate } from 'app/I18N';
import { ClientTranslationContextSchema, ClientTranslationSchema } from 'app/istore';
diff --git a/app/react/V2/Routes/Settings/Translations/components/TableComponents.tsx b/app/react/V2/Routes/Settings/Translations/components/TableComponents.tsx
index 3d12001da0..afc18e1f17 100644
--- a/app/react/V2/Routes/Settings/Translations/components/TableComponents.tsx
+++ b/app/react/V2/Routes/Settings/Translations/components/TableComponents.tsx
@@ -1,6 +1,6 @@
/* eslint-disable react/no-multi-comp */
import React from 'react';
-import { Link } from 'react-router-dom';
+import { Link } from 'react-router';
import { CellContext } from '@tanstack/react-table';
import { Translate } from 'app/I18N';
import { Button, Pill } from 'V2/Components/UI';
diff --git a/app/react/V2/Routes/Settings/Users/Users.tsx b/app/react/V2/Routes/Settings/Users/Users.tsx
index 52a73c3099..62d9d20aa6 100644
--- a/app/react/V2/Routes/Settings/Users/Users.tsx
+++ b/app/react/V2/Routes/Settings/Users/Users.tsx
@@ -1,7 +1,7 @@
/* eslint-disable max-lines */
import React, { useRef, useState } from 'react';
import { IncomingHttpHeaders } from 'http';
-import { ActionFunction, LoaderFunction, useFetcher, useLoaderData } from 'react-router-dom';
+import { ActionFunction, LoaderFunction, useFetcher, useLoaderData } from 'react-router';
import { Translate } from 'app/I18N';
import { Button, ConfirmationModal, Table, Tabs } from 'V2/Components/UI';
import * as usersAPI from 'V2/api/users';
@@ -52,7 +52,7 @@ const Users = () => {
setSidepanelData(group);
});
- const handleBulkAction = () => {
+ const handleBulkAction = async () => {
const formData = new FormData();
formData.set('intent', bulkActionIntent.current || '');
@@ -64,7 +64,7 @@ const Users = () => {
formData.set('confirmation', password.current || '');
- fetcher.submit(formData, { method: 'post' });
+ await fetcher.submit(formData, { method: 'post' });
};
return (
@@ -216,9 +216,9 @@ const Users = () => {
['bulk-reset-2fa', 'delete-users'].includes(bulkActionIntent.current || '')) ||
false
}
- onAcceptClick={value => {
+ onAcceptClick={async value => {
password.current = value;
- handleBulkAction();
+ await handleBulkAction();
setShowConfirmationModal(false);
setSelectedGroups([]);
setSelectedUsers([]);
diff --git a/app/react/V2/Routes/Settings/Users/components/GroupFormSidepanel.tsx b/app/react/V2/Routes/Settings/Users/components/GroupFormSidepanel.tsx
index faa6286c12..6b3a1c89d6 100644
--- a/app/react/V2/Routes/Settings/Users/components/GroupFormSidepanel.tsx
+++ b/app/react/V2/Routes/Settings/Users/components/GroupFormSidepanel.tsx
@@ -1,7 +1,7 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
import { useForm } from 'react-hook-form';
-import { useFetcher } from 'react-router-dom';
+import { useFetcher } from 'react-router';
import { Translate } from 'app/I18N';
import { Button, Card, Sidepanel } from 'V2/Components/UI';
import { InputField, MultiSelect } from 'V2/Components/Forms';
@@ -80,7 +80,7 @@ const GroupFormSidepanel = ({
}
formData.set('data', JSON.stringify(formattedData));
- fetcher.submit(formData, { method: 'post' });
+ await fetcher.submit(formData, { method: 'post' });
closeSidepanel();
};
diff --git a/app/react/V2/Routes/Settings/Users/components/UserFormSidepanel.tsx b/app/react/V2/Routes/Settings/Users/components/UserFormSidepanel.tsx
index acd63c9e65..9008839c84 100644
--- a/app/react/V2/Routes/Settings/Users/components/UserFormSidepanel.tsx
+++ b/app/react/V2/Routes/Settings/Users/components/UserFormSidepanel.tsx
@@ -3,7 +3,7 @@
/* eslint-disable react/jsx-props-no-spreading */
import React, { useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
-import { useFetcher } from 'react-router-dom';
+import { useFetcher } from 'react-router';
import { FetchResponseError } from 'shared/JSONRequest';
import { t, Translate } from 'app/I18N';
import { InputField, Select, MultiSelect } from 'V2/Components/Forms';
@@ -149,15 +149,15 @@ const UserFormSidepanel = ({
formData.set('data', JSON.stringify(data));
formData.set('confirmation', password.current || '');
- fetcher.submit(formData, { method: 'post' });
+ await fetcher.submit(formData, { method: 'post' });
};
- const onClickSubmit = () => {
+ const onClickSubmit = async () => {
const formData = new FormData();
formData.set('intent', actionType.current || '');
formData.set('data', JSON.stringify(selectedUser));
formData.set('confirmation', password.current || '');
- fetcher.submit(formData, { method: 'post' });
+ await fetcher.submit(formData, { method: 'post' });
};
return (
@@ -248,9 +248,9 @@ const UserFormSidepanel = ({
{
+ onClick={async () => {
actionType.current = 'reset-password';
- onClickSubmit();
+ await onClickSubmit();
}}
>
Reset Password
@@ -338,7 +338,7 @@ const UserFormSidepanel = ({
body="Confirm action"
usePassword
onCancelClick={() => setShowConfirmationModal(false)}
- onAcceptClick={value => {
+ onAcceptClick={async value => {
password.current = value;
if (actionType.current === 'formSubmit' && formSubmitRef.current) {
@@ -346,7 +346,7 @@ const UserFormSidepanel = ({
formSubmitRef.current.click();
formSubmitRef.current.disabled = true;
} else {
- onClickSubmit();
+ await onClickSubmit();
}
setShowConfirmationModal(false);
diff --git a/app/react/V2/Routes/Settings/Users/useHandleNotifications.tsx b/app/react/V2/Routes/Settings/Users/useHandleNotifications.tsx
index 327aaa467b..2fc97e396e 100644
--- a/app/react/V2/Routes/Settings/Users/useHandleNotifications.tsx
+++ b/app/react/V2/Routes/Settings/Users/useHandleNotifications.tsx
@@ -1,5 +1,5 @@
import React, { useEffect } from 'react';
-import { useFetchers } from 'react-router-dom';
+import { useFetchers } from 'react-router';
import { useSetAtom } from 'jotai';
import { last } from 'lodash';
import { Translate } from 'app/I18N';
diff --git a/app/react/V2/api/translations/index.ts b/app/react/V2/api/translations/index.ts
index 91ed0ef745..63337ddd8f 100644
--- a/app/react/V2/api/translations/index.ts
+++ b/app/react/V2/api/translations/index.ts
@@ -1,4 +1,4 @@
-import { Params } from 'react-router-dom';
+import { Params } from 'react-router';
import { IncomingHttpHeaders } from 'http';
import api from 'app/utils/api';
import { I18NApi } from 'app/I18N';
diff --git a/app/react/Viewer/components/Paginator.js b/app/react/Viewer/components/Paginator.js
index f59f3033ce..3be63e3406 100644
--- a/app/react/Viewer/components/Paginator.js
+++ b/app/react/Viewer/components/Paginator.js
@@ -1,7 +1,7 @@
/* eslint-disable react/jsx-props-no-spreading */
import PropTypes from 'prop-types';
import React from 'react';
-import { useSearchParams } from 'react-router-dom';
+import { useSearchParams } from 'react-router';
import { Translate } from 'app/I18N';
import { CurrentLocationLink } from 'app/Layout';
import { searchParamsFromSearchParams } from 'app/utils/routeHelpers';
diff --git a/app/react/Viewer/components/specs/Paginator.spec.js b/app/react/Viewer/components/specs/Paginator.spec.js
index d53c6ed949..a82d1468af 100644
--- a/app/react/Viewer/components/specs/Paginator.spec.js
+++ b/app/react/Viewer/components/specs/Paginator.spec.js
@@ -17,8 +17,8 @@ const mockUseSearchParams = jest.fn().mockImplementation(() => {
return [params];
});
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
+jest.mock('react-router', () => ({
+ ...jest.requireActual('react-router'),
useSearchParams: () => mockUseSearchParams(),
useLocation: () => mockUseLocation(),
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/prop-types
diff --git a/app/react/Viewer/specs/PDFView.spec.js b/app/react/Viewer/specs/PDFView.spec.js
index 45ac02ace4..ef0c890972 100644
--- a/app/react/Viewer/specs/PDFView.spec.js
+++ b/app/react/Viewer/specs/PDFView.spec.js
@@ -48,8 +48,8 @@ const mockUseSearchParams = jest.fn().mockImplementation(() => {
return [params];
});
const mockNavigate = jest.fn().mockImplementation(path => path);
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
+jest.mock('react-router', () => ({
+ ...jest.requireActual('react-router'),
useSearchParams: () => mockUseSearchParams(),
useLocation: () => mockUseLocation(),
useNavigate: () => path => mockNavigate(path),
diff --git a/app/react/appRoutes.js b/app/react/appRoutes.js
new file mode 100644
index 0000000000..539a7be064
--- /dev/null
+++ b/app/react/appRoutes.js
@@ -0,0 +1,5 @@
+import { getRoutes } from './Routes';
+import { settingsAtom, atomStore, userAtom } from './V2/atoms';
+
+export const routes =
+ getRoutes && getRoutes(atomStore.get(settingsAtom), atomStore.get(userAtom)?._id);
diff --git a/app/react/componentWrappers.tsx b/app/react/componentWrappers.tsx
index 04b3607ffe..5660171136 100644
--- a/app/react/componentWrappers.tsx
+++ b/app/react/componentWrappers.tsx
@@ -8,7 +8,7 @@ import {
useOutlet,
useParams,
useSearchParams,
-} from 'react-router-dom';
+} from 'react-router';
import { AppMainContext } from './App/AppMainContext';
import { searchParamsFromSearchParams } from './utils/routeHelpers';
diff --git a/app/react/entry-client.tsx b/app/react/entry-client.tsx
index bedeb6d758..8297154326 100644
--- a/app/react/entry-client.tsx
+++ b/app/react/entry-client.tsx
@@ -8,16 +8,17 @@ import {
matchRoutes,
useLocation,
useNavigationType,
-} from 'react-router-dom';
+} from 'react-router';
import { Provider } from 'jotai';
import { Provider as ReduxProvider } from 'react-redux';
import type { RequestError } from 'V2/shared/errorUtils';
import { ErrorBoundary } from './V2/Components/ErrorHandling';
import './App/sockets';
-import { getRoutes } from './Routes';
import CustomProvider from './App/Provider';
-import { settingsAtom, atomStore, userAtom } from './V2/atoms';
+import { atomStore } from './V2/atoms';
import { store } from './store';
+import { options } from './reactRouterConfig';
+import { routes } from './appRoutes';
declare global {
interface Window {
@@ -47,16 +48,14 @@ if (window.SENTRY_APP_DSN) {
});
}
-const router = createBrowserRouter(
- getRoutes(atomStore.get(settingsAtom), atomStore.get(userAtom)?._id)
-);
+const router = createBrowserRouter(routes, options);
const App = () => (
-
+
diff --git a/app/react/entry-server.tsx b/app/react/entry-server.tsx
index f7eba356a7..7ff68e7ad2 100644
--- a/app/react/entry-server.tsx
+++ b/app/react/entry-server.tsx
@@ -3,8 +3,14 @@
import { Request as ExpressRequest, Response } from 'express';
// eslint-disable-next-line node/no-restricted-import
import fs from 'fs';
-import { AgnosticDataRouteObject, createStaticHandler } from '@remix-run/router';
-import { matchRoutes, RouteObject } from 'react-router-dom';
+import {
+ createStaticHandler,
+ createStaticRouter,
+ matchRoutes,
+ RouteObject,
+ StaticHandlerContext,
+ StaticRouterProvider,
+} from 'react-router';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { Helmet } from 'react-helmet';
@@ -13,7 +19,6 @@ import { omit, isEmpty } from 'lodash';
import { Provider as ReduxProvider } from 'react-redux';
import api from 'app/utils/api';
import { RequestParams } from 'app/utils/RequestParams';
-import { createStaticRouter, StaticRouterProvider } from 'react-router-dom/server';
import { FetchResponseError } from 'shared/JSONRequest';
import { ClientSettings } from 'app/apiResponseTypes';
import translationsApi, { IndexedTranslations } from '../api/i18n/translations';
@@ -28,6 +33,7 @@ import { I18NUtils } from './I18N';
import { IStore } from './istore';
import { getRoutes } from './Routes';
import createReduxStore from './store';
+import { options } from './reactRouterConfig';
api.APIURL(`http://localhost:${process.env.PORT || 3000}/api/`);
@@ -254,10 +260,10 @@ const getSSRProperties = async (
language?: string
) => {
const { reduxStore, atomStoreData } = await prepareStores(req, settings, language);
- const { query } = createStaticHandler(routes as AgnosticDataRouteObject[]);
const { fetchRequest, ssrError } = createFetchRequest(req);
+ const { query } = createStaticHandler(routes);
const staticHandleContext = await query(fetchRequest);
- const router = createStaticRouter(routes, staticHandleContext as any);
+ const router = createStaticRouter(routes, staticHandleContext as StaticHandlerContext, options);
const reduxState = reduxStore.getState();
return {
diff --git a/app/react/getIndexElement.tsx b/app/react/getIndexElement.tsx
index 7b7c481810..3bc354e27f 100644
--- a/app/react/getIndexElement.tsx
+++ b/app/react/getIndexElement.tsx
@@ -1,12 +1,13 @@
-import React from 'react';
-import { Navigate } from 'react-router-dom';
import { ClientSettings } from 'app/apiResponseTypes';
-import { validateHomePageRoute } from './utils/routeHelpers';
-import { PageView } from './Pages/PageView';
-import { LibraryTable } from './Library/LibraryTable';
+import React from 'react';
+import { Navigate } from 'react-router';
+import LibraryRoot from './Library/Library';
+import { LibraryCards } from './Library/LibraryCards';
import { LibraryMap } from './Library/LibraryMap';
-import { LibraryCards } from './Library/Library';
+import { LibraryTable } from './Library/LibraryTable';
+import { PageView } from './Pages/PageView';
import { Login } from './Users/Login';
+import { validateHomePageRoute } from './utils/routeHelpers';
import { ViewerRoute } from './Viewer/ViewerRoute';
const deconstructSearchQuery = (query?: string) => {
@@ -23,14 +24,26 @@ const getCustomLibraryPage = (customHomePage: string[]) => {
const queryString = query ? searchQuery : '';
if (customHomePage.includes('map')) {
- return ;
+ return (
+
+
+
+ );
}
if (customHomePage.includes('table')) {
- return ;
+ return (
+
+
+
+ );
}
- return ;
+ return (
+
+
+
+ );
};
const getLibraryDefault = (
@@ -48,14 +61,26 @@ const getLibraryDefault = (
switch (defaultLibraryView) {
case 'table':
- return ;
+ return (
+
+
+
+ );
case 'map':
- return ;
+ return (
+
+
+
+ );
case 'cards':
default:
- return ;
+ return (
+
+
+
+ );
}
};
@@ -65,7 +90,7 @@ const getIndexElement = (settings: ClientSettings | undefined, userId: string |
const isValidHomePage = validateHomePageRoute(settings?.home_page || '');
let element = ;
let parameters;
-
+ let defaultToLibrary = true;
switch (true) {
case !isValidHomePage || customHomePage.length === 0:
element = getLibraryDefault(userId, settings?.defaultLibraryView, settings?.private);
@@ -76,6 +101,7 @@ const getIndexElement = (settings: ClientSettings | undefined, userId: string |
const pageId = customHomePage[customHomePage.indexOf('page') + 1];
element = ;
parameters = { sharedId: pageId };
+ defaultToLibrary = false;
}
break;
@@ -83,6 +109,7 @@ const getIndexElement = (settings: ClientSettings | undefined, userId: string |
{
const pageId = customHomePage[customHomePage.indexOf('entity') + 1];
element = ;
+ defaultToLibrary = false;
}
break;
@@ -94,7 +121,7 @@ const getIndexElement = (settings: ClientSettings | undefined, userId: string |
break;
}
- return { element, parameters };
+ return { element, parameters, defaultToLibrary };
};
export { getIndexElement };
diff --git a/app/react/reactRouterConfig.ts b/app/react/reactRouterConfig.ts
new file mode 100644
index 0000000000..431cfa8924
--- /dev/null
+++ b/app/react/reactRouterConfig.ts
@@ -0,0 +1,9 @@
+const options = {
+ future: {
+ v7_relativeSplatPath: true,
+ v7_partialHydration: true,
+ v7_startTransition: true,
+ },
+};
+
+export { options };
diff --git a/app/react/stories/Paginator.stories.tsx b/app/react/stories/Paginator.stories.tsx
index 42b45c6e8b..8168be8785 100644
--- a/app/react/stories/Paginator.stories.tsx
+++ b/app/react/stories/Paginator.stories.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { MemoryRouter } from 'react-router-dom';
+import { MemoryRouter } from 'react-router';
import type { Meta, StoryObj } from '@storybook/react';
import { Paginator } from 'app/V2/Components/UI';
diff --git a/app/react/utils/api.js b/app/react/utils/api.js
index 4e5776d68c..1ec002174e 100644
--- a/app/react/utils/api.js
+++ b/app/react/utils/api.js
@@ -1,4 +1,4 @@
-import { redirect } from 'react-router-dom';
+import { redirect } from 'react-router';
import { isClient } from 'app/utils';
import { notify } from 'app/Notifications/actions/notificationsActions';
import { store } from 'app/store';
diff --git a/app/react/utils/routeHelpers.ts b/app/react/utils/routeHelpers.ts
index 8422cc0cc7..daa7e59807 100644
--- a/app/react/utils/routeHelpers.ts
+++ b/app/react/utils/routeHelpers.ts
@@ -1,4 +1,4 @@
-import { Location } from 'react-router-dom';
+import { Location } from 'react-router';
import { risonDecodeOrIgnore } from 'app/utils';
import { isArray } from 'lodash';
diff --git a/app/react/utils/specs/api.spec.js b/app/react/utils/specs/api.spec.js
index d7cea4c5fc..aac853f4a0 100644
--- a/app/react/utils/specs/api.spec.js
+++ b/app/react/utils/specs/api.spec.js
@@ -11,8 +11,8 @@ import * as notifyActions from 'app/Notifications/actions/notificationsActions';
const mockRedirect = jest.fn();
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
+jest.mock('react-router', () => ({
+ ...jest.requireActual('react-router'),
redirect: jest.fn().mockImplementation(path => mockRedirect(path)),
}));
diff --git a/app/react/utils/test/renderConnected.tsx b/app/react/utils/test/renderConnected.tsx
index d83b8c37e6..d2f27e8a53 100644
--- a/app/react/utils/test/renderConnected.tsx
+++ b/app/react/utils/test/renderConnected.tsx
@@ -1,12 +1,11 @@
import React from 'react';
import { mount, ReactWrapper, shallow } from 'enzyme';
import configureStore, { MockStore, MockStoreCreator } from 'redux-mock-store';
-import { InitialEntry } from '@remix-run/router';
+import { BrowserRouter, MemoryRouter, InitialEntry } from 'react-router';
import { ConnectedComponent, Provider } from 'react-redux';
import thunk from 'redux-thunk';
import Immutable from 'immutable';
import { render, RenderResult } from '@testing-library/react';
-import { BrowserRouter, MemoryRouter } from 'react-router-dom';
const middlewares = [thunk];
const mockStoreCreator: MockStoreCreator = configureStore(middlewares);
diff --git a/app/setUpJestClient.js b/app/setUpJestClient.js
index 05aa88079f..0018174d49 100644
--- a/app/setUpJestClient.js
+++ b/app/setUpJestClient.js
@@ -1,7 +1,10 @@
/* eslint-disable import/no-extraneous-dependencies */
import '@testing-library/jest-dom';
+import { TextEncoder, TextDecoder } from 'util';
import Adapter from '@cfaester/enzyme-adapter-react-18';
+Object.assign(global, { TextDecoder, TextEncoder });
+
const { configure } = require('enzyme');
configure({ adapter: new Adapter() });
diff --git a/cypress/e2e/__image_snapshots__/Library responsive view Entity view should navigate to the first entity and see the sidepanel #0.png b/cypress/e2e/__image_snapshots__/Library responsive view Entity view should navigate to the first entity and see the sidepanel #0.png
new file mode 100644
index 0000000000..51eb432ce3
Binary files /dev/null and b/cypress/e2e/__image_snapshots__/Library responsive view Entity view should navigate to the first entity and see the sidepanel #0.png differ
diff --git a/cypress/e2e/__image_snapshots__/Library responsive view Toolbar should open the actions bar #0.png b/cypress/e2e/__image_snapshots__/Library responsive view Toolbar should open the actions bar #0.png
new file mode 100644
index 0000000000..e8bd685bbe
Binary files /dev/null and b/cypress/e2e/__image_snapshots__/Library responsive view Toolbar should open the actions bar #0.png differ
diff --git a/cypress/e2e/__image_snapshots__/Library responsive view Toolbar should open the toolbar #0.png b/cypress/e2e/__image_snapshots__/Library responsive view Toolbar should open the toolbar #0.png
new file mode 100644
index 0000000000..e8fa008939
Binary files /dev/null and b/cypress/e2e/__image_snapshots__/Library responsive view Toolbar should open the toolbar #0.png differ
diff --git a/cypress/e2e/__image_snapshots__/Library responsive view Toolbar show sould the filters sidepanel #0.png b/cypress/e2e/__image_snapshots__/Library responsive view Toolbar show sould the filters sidepanel #0.png
new file mode 100644
index 0000000000..9aa5ace47f
Binary files /dev/null and b/cypress/e2e/__image_snapshots__/Library responsive view Toolbar show sould the filters sidepanel #0.png differ
diff --git a/cypress/e2e/attachments.cy.ts b/cypress/e2e/attachments.cy.ts
index f4520ee8ae..18a9034aed 100644
--- a/cypress/e2e/attachments.cy.ts
+++ b/cypress/e2e/attachments.cy.ts
@@ -95,6 +95,10 @@ describe('attachments', () => {
it('should navigate to the spanish document when language is spanish', () => {
cy.contains('a', 'Library').click();
+ cy.contains(
+ 'h2.item-name',
+ 'Artavia Murillo y otros. Resolución de la Corte IDH de 31 de marzo de 2014'
+ );
cy.get('.menuNav-language').click();
cy.get('ul.dropdown-menu.expanded').within(() => {
cy.contains('a', 'Español').click();
diff --git a/cypress/e2e/helpers/index.ts b/cypress/e2e/helpers/index.ts
index 31484cb18b..a0d6b6cd5a 100644
--- a/cypress/e2e/helpers/index.ts
+++ b/cypress/e2e/helpers/index.ts
@@ -10,3 +10,4 @@ export {
saveEntity,
} from './entities';
export { editPropertyForExtractor } from './information-extraction';
+export { waitForLegacyNotifications } from './notifications';
diff --git a/cypress/e2e/helpers/notifications.ts b/cypress/e2e/helpers/notifications.ts
new file mode 100644
index 0000000000..65182ece0a
--- /dev/null
+++ b/cypress/e2e/helpers/notifications.ts
@@ -0,0 +1,7 @@
+const waitForLegacyNotifications = () => {
+ cy.get('.alert-wrapper').each(element => {
+ cy.wrap(element).should('be.empty');
+ });
+};
+
+export { waitForLegacyNotifications };
diff --git a/cypress/e2e/library-responsiveness.cy.ts b/cypress/e2e/library-responsiveness.cy.ts
new file mode 100644
index 0000000000..f9741f6742
--- /dev/null
+++ b/cypress/e2e/library-responsiveness.cy.ts
@@ -0,0 +1,68 @@
+/* eslint-disable max-lines */
+/* eslint-disable max-statements */
+import { clearCookiesAndLogin } from './helpers/login';
+
+const viewport = {
+ viewportWidth: 375,
+ viewportHeight: 667,
+};
+
+// This test is a migration of e2e/mobile/sidePanels.test.ts,
+// it partially covers library responsivenes.
+// e2e/mobile/library.test.ts should be integrated into this test
+describe('Library responsive view', () => {
+ before(() => {
+ const env = { DATABASE_NAME: 'uwazi_e2e', INDEX_NAME: 'uwazi_e2e' };
+ cy.exec('yarn e2e-fixtures', { env });
+ clearCookiesAndLogin('admin', 'admin');
+ });
+
+ describe('Toolbar', viewport, () => {
+ it('should open the toolbar', () => {
+ cy.contains('button', 'Show toolbar').realTouch();
+ cy.get('div.search-list').should('be.visible');
+ cy.get('#root').toMatchImageSnapshot();
+ });
+
+ it('show sould the filters sidepanel', () => {
+ cy.contains('button', 'Show filters').realTouch();
+ cy.contains('.sidepanel-title', 'Filters');
+ cy.get('#root').toMatchImageSnapshot();
+ cy.get('.closeSidepanel.only-mobile').realTouch();
+ cy.contains('button', 'Hide toolbar').realTouch();
+ });
+
+ it('should open the actions bar', () => {
+ cy.contains('button', 'Show actions').realTouch();
+ cy.get('#root').toMatchImageSnapshot();
+ cy.contains('button', 'Hide actions').realTouch();
+ });
+ });
+
+ describe('Entity view', viewport, () => {
+ it('should navigate to the first entity and see the sidepanel', () => {
+ cy.contains(
+ '.item-document',
+ 'Artavia Murillo y otros. Resolución de la CorteIDH de 26 de febrero de 2016'
+ )
+ .contains('a', 'View')
+ .realTouch();
+
+ cy.get('.side-panel.metadata-sidepanel.is-active').should('be.visible');
+ cy.get('#root').toMatchImageSnapshot();
+ });
+
+ it('should check some of the metadata', () => {
+ cy.get('.side-panel.metadata-sidepanel.is-active').within(() => {
+ cy.contains(
+ '.item-name',
+ 'Artavia Murillo y otros. Resolución de la CorteIDH de 26 de febrero de 2016'
+ );
+ cy.contains('dl', 'País').contains('a', 'Costa Rica');
+ cy.contains('dl', 'Firmantes').contains('a', 'Eduardo Vio Grossi');
+ cy.get('.filelist').scrollIntoView();
+ cy.contains('div', 'SamplePDF.pdf').should('be.visible');
+ });
+ });
+ });
+});
diff --git a/cypress/e2e/pdf-display.cy.ts b/cypress/e2e/pdf-display.cy.ts
index afbb5ddde4..41d2dbd74a 100644
--- a/cypress/e2e/pdf-display.cy.ts
+++ b/cypress/e2e/pdf-display.cy.ts
@@ -1,7 +1,12 @@
/* eslint-disable max-lines */
/* eslint-disable max-statements */
import { clearCookiesAndLogin } from './helpers/login';
-import { clickOnCreateEntity, editPropertyForExtractor, saveEntity } from './helpers';
+import {
+ clickOnCreateEntity,
+ editPropertyForExtractor,
+ saveEntity,
+ waitForLegacyNotifications,
+} from './helpers';
describe('PDF display', () => {
before(() => {
@@ -32,6 +37,7 @@ describe('PDF display', () => {
force: true,
});
saveEntity();
+ waitForLegacyNotifications();
cy.get('.metadata-sidepanel.is-active .closeSidepanel').click();
});
});
@@ -52,17 +58,19 @@ describe('PDF display', () => {
it('should paginate forward', () => {
cy.get('.paginator').within(() => {
- cy.contains('a', 'Next').click();
+ cy.contains('a', 'Next').realClick();
});
cy.contains('Los escritos de 17 de septiembre y 17 de noviembre de 2010,');
cy.get('.paginator').within(() => {
- cy.contains('a', 'Next').click();
+ cy.contains('2 / 22');
+ cy.contains('a', 'Next').realClick();
});
cy.contains('especial de protección de los beneficiarios de las medidas,');
cy.get('.paginator').within(() => {
- cy.contains('a', 'Next').click();
+ cy.contains('3 / 22');
+ cy.contains('a', 'Next').realClick();
});
cy.contains('En la presente Resolución el Tribunal examinará:');
cy.contains('CORTE INTERAMERICANA DE DERECHOS HUMANOS').should('not.exist');
@@ -75,14 +83,15 @@ describe('PDF display', () => {
it('should paginate backwards', () => {
cy.get('.paginator').within(() => {
- cy.contains('a', 'Previous').click();
+ cy.contains('4 / 22');
+ cy.contains('a', 'Previous').realClick();
});
cy.contains('especial de protección de los beneficiarios de las medidas,');
cy.contains('En la presente Resolución el Tribunal examinará:').should('not.be.visible');
});
it('should show the plaintex for the page', () => {
- cy.contains('a', 'Plain text').click();
+ cy.contains('a', 'Plain text').realClick();
cy.get('.raw-text').should('be.visible');
cy.get('.raw-text').within(() => {
cy.contains('-3especial de protección');
@@ -91,7 +100,7 @@ describe('PDF display', () => {
it('should paginate in plain text view', () => {
cy.get('.paginator').within(() => {
- cy.contains('a', 'Next').click();
+ cy.contains('a', 'Next').realClick();
});
cy.get('.raw-text').within(() => {
cy.contains('-4-');
@@ -195,23 +204,21 @@ describe('PDF display', () => {
});
});
- describe('responsiveness', () => {
+ describe('responsiveness', { viewportWidth: 768, viewportHeight: 1024 }, () => {
describe('library', () => {
- beforeEach(() => {
- cy.viewport('ipad-mini');
- });
-
it('should navigate to the library', () => {
cy.get('header').within(() => {
cy.get('.menu-button').realTouch();
});
cy.contains('a', 'Library').realTouch();
+ cy.contains('.item-document', 'Entity with pdf');
});
it('should view the pdf correctly', () => {
cy.contains('.item-document', 'Entity with pdf').within(() => {
cy.contains('a', 'View').realTouch();
});
+ cy.contains('CORTE INTERAMERICANA DE DERECHOS HUMANOS');
cy.get('.closeSidepanel').realTouch();
cy.get('aside.metadata-sidepanel').should('not.be.visible');
cy.contains('CORTE INTERAMERICANA DE DERECHOS HUMANOS').should('be.visible');
@@ -232,11 +239,7 @@ describe('PDF display', () => {
});
});
- describe('IX sidepanel', () => {
- beforeEach(() => {
- cy.viewport('iphone-x');
- });
-
+ describe('IX sidepanel', { viewportWidth: 375, viewportHeight: 812 }, () => {
it('should navigate to the extractor', () => {
cy.get('header').within(() => {
cy.get('.menu-button').realTouch();
@@ -263,7 +266,7 @@ describe('PDF display', () => {
cy.get('#root').toMatchImageSnapshot();
});
- it('should check page rendering', () => {
+ it('should only show visible pages', () => {
cy.get('#page-1-container .page').should('be.empty');
cy.get('#page-2-container .page').should('not.be.empty');
cy.get('#page-3-container .page').should('not.be.empty');
diff --git a/cypress/e2e/private-instance.cy.ts b/cypress/e2e/private-instance.cy.ts
new file mode 100644
index 0000000000..84c7823e9a
--- /dev/null
+++ b/cypress/e2e/private-instance.cy.ts
@@ -0,0 +1,46 @@
+/* eslint-disable max-lines */
+/* eslint-disable max-statements */
+import { clearCookiesAndLogin } from './helpers/login';
+
+describe('Private instance', () => {
+ before(() => {
+ cy.blankState();
+ cy.clearAllCookies();
+ });
+
+ it('should be redirected to the login screen by default', () => {
+ cy.visit('http://localhost:3000');
+ cy.contains('Login');
+ cy.location('pathname').should('eq', '/login');
+ });
+
+ it('should login an be able to see the library', () => {
+ clearCookiesAndLogin('admin', 'change this password now');
+ cy.contains('.blank-state', 'Welcome to Uwazi');
+ });
+
+ it('should navigate to the settings and make the instance public', () => {
+ cy.contains('a', 'Settings').realClick();
+ cy.contains('a', 'Collection').realClick();
+ cy.contains('div', 'Public instance').within(() => {
+ cy.get('input[type="checkbox"]').check({ force: true });
+ cy.contains('div', 'Disable');
+ });
+ cy.contains('button', 'Save').realClick();
+ cy.contains('div', 'Settings updated.');
+ cy.contains('button', 'Dismiss').realClick();
+ });
+
+ it('should logout and still be able to see the library', () => {
+ // UWAZI homepage has some errors on the console when there are not entities.
+ // This errors have no impact on the user and it's not withing the scope of this PR to fix them
+ cy.on('uncaught:exception', () => false);
+
+ cy.contains('a', 'Account').click();
+ cy.get('[data-testid="settings-account"]').within(() => {
+ cy.contains('a', 'Logout').realClick();
+ });
+ cy.contains('.blank-state', 'Welcome to Uwazi');
+ cy.contains('div.sidepanel-title', 'Filters');
+ });
+});
diff --git a/e2e/mobile/sidePanels.test.ts b/e2e/mobile/sidePanels.test.ts
deleted file mode 100644
index 5f84623ffc..0000000000
--- a/e2e/mobile/sidePanels.test.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { adminLogin, logout } from '../helpers/login';
-import proxyMock from '../helpers/proxyMock';
-import insertFixtures from '../helpers/insertFixtures';
-import disableTransitions from '../helpers/disableTransitions';
-import { getContentBySelector } from '../helpers/selectorUtils';
-
-const selectors = {
- searchInLibrary: '.library-header .library-toolbar .header-bottom .toggle-button.only-mobile',
- sidePanelFiltersTitle: '.sidepanel-title > div > span',
- firstEntityView: '.documents-list > div > .item-group > div > .item-actions > div > a > span',
-};
-
-describe('Side panels', () => {
- beforeAll(async () => {
- await insertFixtures();
- await proxyMock();
- await adminLogin();
- await expect(page).toClick('a.public-documents');
- // iphone 6 measurements
- await page.setViewport({ width: 376, height: 667 });
- await disableTransitions();
- });
-
- afterAll(async () => {
- await logout();
- });
-
- describe('library view', () => {
- it('when clicking on the search button a side panel should appear', async () => {
- await expect(page).toClick('.open-toolbar-button .toggle-toolbar-button');
- await expect(page).toClick(selectors.searchInLibrary);
- await expect(page).toMatchElement(selectors.sidePanelFiltersTitle, { text: 'Filters' });
- await expect(page).toClick('button[aria-label="Close side panel"]');
- await expect(page).toClick('.close-toolbar-button .toggle-toolbar-button');
- });
- });
-
- describe('Entity view', () => {
- it('should show attachments', async () => {
- await expect(page).toClick(selectors.firstEntityView);
- await expect(page).toMatchElement('div.file > div.file-originalname');
- const [filename] = await getContentBySelector('div.file > div.file-originalname');
- expect(filename).toEqual('SamplePDF.pdf');
- });
- });
-});
diff --git a/e2e/regression_suites/entities.test.ts b/e2e/regression_suites/entities.test.ts
index 7f4bd92165..559233d9ef 100644
--- a/e2e/regression_suites/entities.test.ts
+++ b/e2e/regression_suites/entities.test.ts
@@ -59,12 +59,17 @@ describe('Homepage entities', () => {
it('should display the related entity on the sidepanel', async () => {
await page.goto(`${host}/entity/7ycel666l65vobt9`);
+ await disableTransitions();
+ await page.waitForSelector('div[aria-label="Relationships"]');
await expect(page).toClick('div[aria-label="Relationships"]');
await page.waitForSelector('.relationships-graph');
await expect(page).toClick('.item-name', {
text: 'Artavia Murillo y otros. Resolución de la Corte IDH de 31 de marzo de 2014',
});
await page.waitForSelector('aside.side-panel > .sidepanel-body > .view > .item-info');
+ await expect(page).toMatchElement('.item-name', {
+ text: 'Artavia Murillo y otros. Resolución de la Corte IDH de 31 de marzo de 2014',
+ });
await testSelectorShot('aside.side-panel > .sidepanel-body', { threshold: 0.08 });
});
diff --git a/e2e/suite1/metadataprops.test.ts b/e2e/suite1/metadataprops.test.ts
index c8d64d5d8d..7901a2118d 100644
--- a/e2e/suite1/metadataprops.test.ts
+++ b/e2e/suite1/metadataprops.test.ts
@@ -23,6 +23,7 @@ describe('Metadata Properties', () => {
it('should test number of available properties.', async () => {
await expect(page).toClick('a', { text: 'Templates' });
await expect(page).toClick('a', { text: 'Add template' });
+ await expect(page).toMatchElement('span', { text: 'Metadata creator' });
const propertyList = await page.$$('.property-options-list li');
expect(propertyList.length).toBe(13);
});
diff --git a/e2e/suite2/smokePrivateInstance.test.ts b/e2e/suite2/smokePrivateInstance.test.ts
deleted file mode 100644
index 45a25e5cd1..0000000000
--- a/e2e/suite2/smokePrivateInstance.test.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { adminLogin, logout } from '../helpers/login';
-import { host } from '../config';
-import proxyMock from '../helpers/proxyMock';
-import insertFixtures from '../helpers/insertFixtures';
-import disableTransitions from '../helpers/disableTransitions';
-
-const selectors = {
- collection: {
- togglePrivate: 'label[data-testid="enable-button-checkbox"]',
- newNameGeneration:
- '#collection-form > div:nth-child(1) > div:nth-child(2) > div > div:nth-child(9) > label',
- },
-};
-
-describe('Private instance', () => {
- beforeAll(async () => {
- await insertFixtures();
- await proxyMock();
- await disableTransitions();
- });
-
- it('should log in as admin, and toggle private instance in collection settings', async () => {
- await adminLogin();
- await page.goto(`${host}/settings/account`);
- await expect(page).toMatchElement('span', { text: 'Account' });
- await expect(page).toClick('a', { text: 'Settings' });
- await expect(page).toClick('a', { text: 'Collection' });
- await expect(page).toClick(selectors.collection.togglePrivate);
- await expect(page).toClick(selectors.collection.newNameGeneration);
- await expect(page).toClick('button', { text: 'Save' });
- });
-
- it('shoud log out and be redirected to login page instead of library page', async () => {
- await logout();
- await page.goto(`${host}/library`);
- expect(page.url()).toBe(`${host}/login`);
- });
-});
diff --git a/e2e/suite2/tableView.test.ts b/e2e/suite2/tableView.test.ts
index ead494a89c..4aac8bd2d8 100644
--- a/e2e/suite2/tableView.test.ts
+++ b/e2e/suite2/tableView.test.ts
@@ -130,6 +130,7 @@ describe('Table view', () => {
await expect(page.click('.btn-load-more'));
await page.waitForNavigation();
await disableTransitions();
+ await expect(page).toMatchElement('span', { text: '60 shown of' });
await page.waitForSelector(rowSelector);
expect((await page.$$(rowSelector)).length).toBe(60);
});
diff --git a/package.json b/package.json
index 3550fa8f66..5518e18890 100644
--- a/package.json
+++ b/package.json
@@ -104,7 +104,6 @@
"@huridocs/react-text-selection-handler": "^0.3.0",
"@loadable/component": "^5.16.4",
"@popperjs/core": "^2.11.8",
- "@remix-run/router": "1.21.1",
"@sentry/node": "^7.114.0",
"@sentry/react": "7.114.0",
"@sentry/tracing": "^7.114.0",
@@ -210,7 +209,7 @@
"react-redux": "5.0.6",
"react-redux-form": "^1.16.14",
"react-render-if-visible": "^2.1.1",
- "react-router-dom": "6.27.0",
+ "react-router": "^7.1.1",
"react-table": "^7.8.0",
"react-table-sticky": "^1.1.3",
"react-tabs": "^6.1.0",
diff --git a/yarn.lock b/yarn.lock
index 8e5b58e725..aca0287b05 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2992,16 +2992,6 @@
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz#d1b4befa423f692fa4abf1c79209702e7d8ae4b4"
integrity sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==
-"@remix-run/router@1.20.0":
- version "1.20.0"
- resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.20.0.tgz#03554155b45d8b529adf635b2f6ad1165d70d8b4"
- integrity sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==
-
-"@remix-run/router@1.21.1":
- version "1.21.1"
- resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.21.1.tgz#bf15274d3856c395402719fa6b1dc8cc5245aaf7"
- integrity sha512-KeBYSwohb8g4/wCcnksvKTYlg69O62sQeLynn2YE+5z7JWEj95if27kclW9QqbrlsQ2DINI8fjbV3zyuKfwjKg==
-
"@rtsao/scc@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
@@ -6868,6 +6858,11 @@ cookie@0.7.2:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
+cookie@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.0.2.tgz#27360701532116bd3f1f9416929d176afe1e4610"
+ integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==
+
cookie@~0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
@@ -14382,20 +14377,15 @@ react-render-if-visible@^2.1.1:
resolved "https://registry.yarnpkg.com/react-render-if-visible/-/react-render-if-visible-2.1.1.tgz#b67d8a6d230d8e1e8986212a1d4098425a26810b"
integrity sha512-dl6OZkF+ktKi/pMNt5Upn99gSWnOCAQdFp/WBclMsPs4dV/RLeE40lab8tixoHB4UEKsspv0jFR13HF4fxmPHw==
-react-router-dom@6.27.0:
- version "6.27.0"
- resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.27.0.tgz#8d7972a425fd75f91c1e1ff67e47240c5752dc3f"
- integrity sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==
- dependencies:
- "@remix-run/router" "1.20.0"
- react-router "6.27.0"
-
-react-router@6.27.0:
- version "6.27.0"
- resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.27.0.tgz#db292474926c814c996c0ff3ef0162d1f9f60ed4"
- integrity sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==
+react-router@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.1.1.tgz#88f5657fa5b8f0b918c7222ec710de0274d00b2e"
+ integrity sha512-39sXJkftkKWRZ2oJtHhCxmoCrBCULr/HAH4IT5DHlgu/Q0FCPV0S4Lx+abjDTx/74xoZzNYDYbOZWlJjruyuDQ==
dependencies:
- "@remix-run/router" "1.20.0"
+ "@types/cookie" "^0.6.0"
+ cookie "^1.0.1"
+ set-cookie-parser "^2.6.0"
+ turbo-stream "2.4.0"
react-shallow-renderer@^16.15.0:
version "16.15.0"
@@ -15195,6 +15185,11 @@ serve-static@1.16.2:
parseurl "~1.3.3"
send "0.19.0"
+set-cookie-parser@^2.6.0:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943"
+ integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==
+
set-function-length@^1.2.1, set-function-length@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
@@ -16385,6 +16380,11 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
+turbo-stream@2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/turbo-stream/-/turbo-stream-2.4.0.tgz#1e4fca6725e90fa14ac4adb782f2d3759a5695f0"
+ integrity sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==
+
tween-functions@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tween-functions/-/tween-functions-1.2.0.tgz#1ae3a50e7c60bb3def774eac707acbca73bbc3ff"