diff --git a/jestSetupAfterEnv.tsx b/jestSetupAfterEnv.tsx
index dd45486f7..2219a795d 100644
--- a/jestSetupAfterEnv.tsx
+++ b/jestSetupAfterEnv.tsx
@@ -169,3 +169,10 @@ jest.mock('./src/react/next-architecture/ui/XCoreLibraryProvider', () => {
});
jest.mock('@module-federation/enhanced/runtime', () => {}, { virtual: true });
+
+jest.mock('@scality/module-federation', () => ({
+ useCurrentApp: () => ({
+ name: 'zenko-ui',
+ appHistoryBasePath: '',
+ }),
+}));
diff --git a/src/QueryClientProvider.tsx b/src/QueryClientProvider.tsx
new file mode 100644
index 000000000..915ba3a36
--- /dev/null
+++ b/src/QueryClientProvider.tsx
@@ -0,0 +1,11 @@
+import {
+ QueryClient,
+ QueryClientProvider as BaseQueryClientProvider,
+} from 'react-query';
+
+export const QueryClientProvider =
+ BaseQueryClientProvider as React.ComponentType<{
+ client: QueryClient;
+ contextSharing?: boolean;
+ children?: React.ReactNode;
+ }>;
diff --git a/src/react/DataServiceRoleProvider.tsx b/src/react/DataServiceRoleProvider.tsx
index 5421234fb..7fe601e46 100644
--- a/src/react/DataServiceRoleProvider.tsx
+++ b/src/react/DataServiceRoleProvider.tsx
@@ -86,7 +86,7 @@ export const useCurrentAccount = () => {
else if (accountId) return account.id === accountId;
else return true;
});
- }, [accountId, JSON.stringify(accounts)]);
+ }, [accountId, JSON.stringify(accounts), accountName]);
return {
account,
@@ -153,7 +153,7 @@ const DataServiceRoleProvider = ({
if (role.roleArn) {
assumeRoleMutation.mutate(role.roleArn);
}
- }, [role.roleArn, JSON.stringify(accounts), userData?.token]);
+ }, [role.roleArn, JSON.stringify(accounts), userData?.token, accountName]);
const { getS3Config } = useS3ConfigFromAssumeRoleResult();
diff --git a/src/react/Routes.tsx b/src/react/Routes.tsx
index 4f535a4b8..e02ba7972 100644
--- a/src/react/Routes.tsx
+++ b/src/react/Routes.tsx
@@ -45,6 +45,7 @@ import LocationEditor from './locations/LocationEditor';
import { useBasenameRelativeNavigate } from './ShellHooksContext';
import Workflows from './workflow/Workflows';
import CreateWorkflow from './workflow/CreateWorkflow';
+import Objects from './databrowser/objects/Objects';
export const RemoveTrailingSlash = ({ ...rest }) => {
const location = useLocation();
@@ -186,6 +187,10 @@ function PrivateRoutes() {
path="accounts/:accountName/create-bucket/*"
element={}
/>
+ }
+ />
}
@@ -214,13 +219,17 @@ function InternalRoutes() {
const doesRouteMatch = useCallback(
(paths: string | string[]) => {
if (Array.isArray(paths)) {
- const foundMatchingRoute = paths.find(
- (path) =>
- !!matchPath(config.basePath + path + '/*', location.pathname),
+ return paths.some((path) =>
+ matchPath(
+ { path: config.basePath + path, end: false },
+ location.pathname,
+ ),
);
- return !!foundMatchingRoute;
} else {
- return !!matchPath(config.basePath + paths + '/*', location.pathname);
+ return !!matchPath(
+ { path: config.basePath + paths, end: false },
+ location.pathname,
+ );
}
},
[location.pathname],
@@ -270,7 +279,8 @@ function InternalRoutes() {
},
active:
doesRouteMatch('/buckets') ||
- doesRouteMatch('/accounts/:accountName/buckets'),
+ doesRouteMatch('/accounts/:accountName/buckets') ||
+ doesRouteMatch('/accounts/:accountName/data/buckets'),
},
{
label: 'Workflows',
@@ -280,7 +290,8 @@ function InternalRoutes() {
},
active:
doesRouteMatch('/workflows') ||
- doesRouteMatch('/accounts/:accountName/workflows'),
+ doesRouteMatch('/accounts/:accountName/workflows') ||
+ doesRouteMatch('/accounts/:accountName/data/workflows'),
},
...(isStorageManager
? [
diff --git a/src/react/account/AccountRoleSelectButtonAndModal.tsx b/src/react/account/AccountRoleSelectButtonAndModal.tsx
index a23d4b3a4..bd8f1348f 100644
--- a/src/react/account/AccountRoleSelectButtonAndModal.tsx
+++ b/src/react/account/AccountRoleSelectButtonAndModal.tsx
@@ -1,6 +1,7 @@
import { Icon, Stack, Tooltip, Wrap } from '@scality/core-ui';
import { Box, Button, Table } from '@scality/core-ui/dist/next';
import { useMemo, useState } from 'react';
+import { useLocation, useNavigate, useParams } from 'react-router-dom';
import {
useCurrentAccount,
useDataServiceRole,
@@ -132,6 +133,8 @@ export function AccountRoleSelectButtonAndModal({
setIsModalOpen(false);
};
+ console.log({ accountName });
+
return (
<>
{
- const navigate = useBasenameRelativeNavigate();
+ const navigateWithBasename = useBasenameRelativeNavigate();
+ const navigate = useNavigate();
+ const { accountName } = useParams();
+ const location = useLocation();
+
+ const handleAccountClick = () => {
+ const replacePath = location.pathname.replace(accountName, assumedAccount);
+
+ if (replacePath.includes('/buckets')) {
+ navigateWithBasename(`/accounts/${assumedAccount}/data/buckets`);
+ } else if (replacePath.includes('/workflows')) {
+ navigateWithBasename(`/accounts/${assumedAccount}/data/workflows`);
+ } else {
+ navigate(replacePath);
+ }
+ };
return (
@@ -201,7 +219,7 @@ const ModalFooter = ({
variant="primary"
onClick={() => {
setRole({ roleArn: assumedRoleArn });
- navigate(`/accounts/${assumedAccount}/buckets`);
+ handleAccountClick();
handleClose();
}}
label="Continue"
diff --git a/src/react/databrowser/buckets/Buckets.tsx b/src/react/databrowser/buckets/Buckets.tsx
index 640e4bb58..080c2b7d7 100644
--- a/src/react/databrowser/buckets/Buckets.tsx
+++ b/src/react/databrowser/buckets/Buckets.tsx
@@ -28,7 +28,7 @@ export default function Buckets() {
(state: AppState) =>
state.instanceStatus.latest.metrics?.['ingest-schedule']?.states,
);
- const { bucketName: bucketNameParam } = useParams<{
+ const { bucketName: bucketNameParam, accountName } = useParams<{
bucketName: string;
accountName: string;
}>();
@@ -85,9 +85,11 @@ export default function Buckets() {
}
// Replace the old bucket name with the new one when switching accounts
+
if (
bucketNameParam &&
- !buckets.value.some((bucket) => bucket.name === bucketNameParam)
+ !buckets.value.some((bucket) => bucket.name === bucketNameParam) &&
+ accountName === account?.Name
) {
return (
{
const [hintsShown, setHintsShown] = useState(false);
- const navigate = useBasenameRelativeNavigate();
+ const navigate = useNavigate();
const query = useQueryParams();
const { pathname } = useLocation();
const prefixWithSlash = usePrefixWithSlash();
diff --git a/src/react/databrowser/objects/ObjectList.tsx b/src/react/databrowser/objects/ObjectList.tsx
index 1919d2770..ebd481ca4 100644
--- a/src/react/databrowser/objects/ObjectList.tsx
+++ b/src/react/databrowser/objects/ObjectList.tsx
@@ -1,25 +1,24 @@
-import * as T from '../../ui-elements/Table';
+import { Icon, spacing, Toggle } from '@scality/core-ui';
+import { Box } from '@scality/core-ui/dist/next';
+import { List } from 'immutable';
+import { useDispatch, useSelector } from 'react-redux';
+import { useLocation, useNavigate } from 'react-router-dom';
import { ListObjectsType, ObjectEntity } from '../../../types/s3';
-import { LIST_OBJECT_VERSIONS_S3_TYPE } from '../../utils/s3';
-import { maybePluralize } from '../../utils';
+import { AppState } from '../../../types/state';
import {
openFolderCreateModal,
openObjectDeleteModal,
openObjectUploadModal,
} from '../../actions';
-import { useDispatch, useSelector } from 'react-redux';
-import { AppState } from '../../../types/state';
-import { List } from 'immutable';
-import MetadataSearch from './MetadataSearch';
-import ObjectListTable from './ObjectListTable';
-import { Icon, spacing, Toggle } from '@scality/core-ui';
-import { WarningMetadata } from '../../ui-elements/Warning';
-import { useQueryParams } from '../../utils/hooks';
-import { useLocation } from 'react-router-dom';
-import { Box } from '@scality/core-ui/dist/next';
import { useBucketVersionning } from '../../next-architecture/domain/business/buckets';
+import * as T from '../../ui-elements/Table';
import { VEEAM_XML_PREFIX } from '../../ui-elements/Veeam/VeeamConstants';
-import { useBasenameRelativeNavigate } from '../../ShellHooksContext';
+import { WarningMetadata } from '../../ui-elements/Warning';
+import { maybePluralize } from '../../utils';
+import { useQueryParams } from '../../utils/hooks';
+import { LIST_OBJECT_VERSIONS_S3_TYPE } from '../../utils/s3';
+import MetadataSearch from './MetadataSearch';
+import ObjectListTable from './ObjectListTable';
type Props = {
objects: List;
bucketName: string;
@@ -35,7 +34,7 @@ export default function ObjectList({
toggled,
listType,
}: Props) {
- const navigate = useBasenameRelativeNavigate();
+ const navigate = useNavigate();
const dispatch = useDispatch();
const { pathname } = useLocation();
const query = useQueryParams();
diff --git a/src/react/databrowser/objects/ObjectRow.tsx b/src/react/databrowser/objects/ObjectRow.tsx
index 249aa13f3..95ab5a0e9 100644
--- a/src/react/databrowser/objects/ObjectRow.tsx
+++ b/src/react/databrowser/objects/ObjectRow.tsx
@@ -1,7 +1,7 @@
import isDeepEqual from 'lodash.isequal';
import memoize from 'memoize-one';
import { memo } from 'react';
-import { useLocation } from 'react-router-dom';
+import { useLocation, useNavigate } from 'react-router-dom';
import { areEqual } from 'react-window';
import { Dispatch } from 'redux';
@@ -10,7 +10,6 @@ import { ObjectEntity } from '../../../types/s3';
import { toggleAllObjects } from '../../actions';
import * as T from '../../ui-elements/Table';
import { useQueryParams } from '../../utils/hooks';
-import { useBasenameRelativeNavigate } from '../../ShellHooksContext';
type PrepareRow = (arg0: RowType) => void;
type RowType = {
@@ -50,7 +49,7 @@ const Row = ({
}: RowProps) => {
const row = rows[index];
prepareRow(row);
- const navigate = useBasenameRelativeNavigate();
+ const navigate = useNavigate();
const { pathname } = useLocation();
const query = useQueryParams();
const versionId = query.get('versionId');
diff --git a/src/react/databrowser/objects/Objects.tsx b/src/react/databrowser/objects/Objects.tsx
index dece330c1..411ecc069 100644
--- a/src/react/databrowser/objects/Objects.tsx
+++ b/src/react/databrowser/objects/Objects.tsx
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
-import { Redirect, useParams } from 'react-router-dom';
+import { Navigate, useParams } from 'react-router-dom';
import {
getObjectMetadata,
listObjects,
@@ -117,7 +117,7 @@ export default function Objects() {
}
if (!bucketNameParam) {
- return ;
+ return ;
}
// TODO: manage empty state
diff --git a/src/react/ui-elements/Breadcrumb.tsx b/src/react/ui-elements/Breadcrumb.tsx
index 304d9c0d0..80a1a9c3e 100644
--- a/src/react/ui-elements/Breadcrumb.tsx
+++ b/src/react/ui-elements/Breadcrumb.tsx
@@ -6,6 +6,7 @@ import {
import styled from 'styled-components';
import AccountRoleSelectButtonAndModal from '../account/AccountRoleSelectButtonAndModal';
import { fontSize } from '@scality/core-ui/dist/style/theme';
+import { useConfig } from '../next-architecture/ui/ConfigProvider';
// vendor from `polished` package
type Styles = {
@@ -224,8 +225,10 @@ export function Breadcrumb({ breadcrumbPaths }: Props) {
}
export function BreadcrumbAccount({ pathname }: { pathname: string }) {
+ const config = useConfig();
+
const matchAccountUserAccessKey = matchPath(
- '/accounts/:accountName/users/:userName/access-keys',
+ config.basePath + '/accounts/:accountName/users/:userName/access-keys',
pathname,
);
@@ -241,7 +244,10 @@ export function BreadcrumbAccount({ pathname }: { pathname: string }) {
);
}
- const matchAccountRoute = matchPath('/accounts/:accountName', pathname);
+ const matchAccountRoute = matchPath(
+ config.basePath + '/accounts/:accountName' + '/*',
+ pathname,
+ );
if (matchAccountRoute) {
return (
@@ -251,7 +257,10 @@ export function BreadcrumbAccount({ pathname }: { pathname: string }) {
);
}
- const matchAllAccountsRoute = matchPath('/accounts/', pathname);
+ const matchAllAccountsRoute = matchPath(
+ config.basePath + '/accounts/',
+ pathname,
+ );
if (matchAllAccountsRoute) {
return (
diff --git a/src/react/ui-elements/PrivateRoute.tsx b/src/react/ui-elements/PrivateRoute.tsx
index 16e95a60a..17d69cc0f 100644
--- a/src/react/ui-elements/PrivateRoute.tsx
+++ b/src/react/ui-elements/PrivateRoute.tsx
@@ -1,4 +1,4 @@
-import { Redirect, Route } from 'react-router-dom';
+import { Navigate, Route } from 'react-router-dom';
import React from 'react';
import { connect } from 'react-redux';
@@ -8,16 +8,7 @@ function PrivateRoute(props) {
if (props.authenticated) {
return ;
} else {
- return (
-
- );
+ return ;
}
}
@@ -28,4 +19,4 @@ function mapStateToProps(state) {
};
}
-export default connect(mapStateToProps)(PrivateRoute);
\ No newline at end of file
+export default connect(mapStateToProps)(PrivateRoute);
diff --git a/src/react/utils/testUtil.tsx b/src/react/utils/testUtil.tsx
index 5d7d8c301..7602118bc 100644
--- a/src/react/utils/testUtil.tsx
+++ b/src/react/utils/testUtil.tsx
@@ -1,14 +1,8 @@
import { ReactWrapper, mount } from 'enzyme';
-import { createMemoryHistory } from 'history';
import { PropsWithChildren, ReactNode } from 'react';
-import {
- QueryClient,
- QueryClientProvider,
- setLogger,
- useMutation,
-} from 'react-query';
+import { QueryClient, setLogger, useMutation } from 'react-query';
import { Provider } from 'react-redux';
-import { Route, Router } from 'react-router-dom';
+import { MemoryRouter, Route } from 'react-router-dom';
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { ThemeProvider } from 'styled-components';
@@ -20,6 +14,7 @@ import { coreUIAvailableThemes } from '@scality/core-ui/dist/style/theme';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { applyMiddleware, compose, createStore } from 'redux';
+import { QueryClientProvider } from '../../QueryClientProvider';
import ZenkoClient from '../../js/ZenkoClient';
import { VEEAM_FEATURE, XDM_FEATURE } from '../../js/config';
import { UiFacingApiWrapper } from '../../js/managementClient';
@@ -43,7 +38,6 @@ import ErrorHandlerModal from '../ui-elements/ErrorHandlerModal';
import ReauthDialog from '../ui-elements/ReauthDialog';
export const theme = coreUIAvailableThemes.darkRebrand;
-export const navigate = createMemoryHistory();
export const configuration = {
latest: {
version: 1,
@@ -193,7 +187,7 @@ export const Wrapper = ({ children }: { children: ReactNode }) => {
-
+
<_ConfigContext.Provider
//@ts-expect-error fix this when you are working on it
value={zenkoUITestConfig}
@@ -231,7 +225,7 @@ export const Wrapper = ({ children }: { children: ReactNode }) => {
-
+
@@ -423,7 +417,6 @@ export function renderWithRouterMatch(
{ path = '/', route = '/' } = {},
testState?: unknown,
) {
- const navigate = createMemoryHistory({ initialEntries: [route] });
const store = realStoreWithInitState(testState);
const role = {
roleArn: TEST_ROLE_ARN,
@@ -435,7 +428,7 @@ export function renderWithRouterMatch(
-
+
<_DataServiceRoleContext.Provider
//@ts-expect-error fix this when you are working on it
@@ -473,7 +466,7 @@ export function renderWithRouterMatch(
-
+
@@ -487,7 +480,6 @@ export const renderWithCustomRoute = (
route: string,
testState?: unknown,
) => {
- const navigate = createMemoryHistory({ initialEntries: [route] });
const store = realStoreWithInitState(testState);
const role = {
roleArn: TEST_ROLE_ARN,
@@ -498,7 +490,7 @@ export const renderWithCustomRoute = (
-
+
<_ConfigContext.Provider
//@ts-expect-error fix this when you are working on it
value={zenkoUITestConfig}
@@ -539,7 +531,7 @@ export const renderWithCustomRoute = (
-
+
,
@@ -572,17 +564,14 @@ const DataServiceProvider = ({ children }) => {
export const NewWrapper =
(route = '/', testState: unknown = {}) =>
({ children }: { children: ReactNode }) => {
- const navigate = createMemoryHistory({ initialEntries: [route] });
const store = realStoreWithInitState(testState);
-
- // const navigate = createMemoryHistory();
// const store = realStoreWithInitState({});
return (
-
+
<_ManagementContext.Provider
@@ -617,7 +606,7 @@ export const NewWrapper =
-
+