Skip to content

Commit dfa56d0

Browse files
fix: Inconsistent handling browser history routes (#3589)
* Prevent to navigate to the same path * Resolve lazy loading routes * Handling incorrect path waiting for extensibility routes * PR correction * PR correction * PR correction * PR correction
1 parent cb3d272 commit dfa56d0

File tree

4 files changed

+110
-61
lines changed

4 files changed

+110
-61
lines changed

src/components/App/ClusterRoutes.js

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect } from 'react';
1+
import { useEffect, useState } from 'react';
22
import { Route, Routes, useSearchParams } from 'react-router-dom';
33
import { useTranslation } from 'react-i18next';
44
import { useRecoilValue, useRecoilState, useSetRecoilState } from 'recoil';
@@ -30,6 +30,17 @@ export default function ClusterRoutes() {
3030
const extensions = useRecoilValue(extensionsState);
3131
const [cluster, setCluster] = useRecoilState(clusterState);
3232
const [search] = useSearchParams();
33+
const [extensibilityRoutes, setExtensibilityRoutes] = useState(null);
34+
35+
useEffect(() => {
36+
if (extensions?.length) {
37+
setExtensibilityRoutes(
38+
extensions?.map(extension =>
39+
createExtensibilityRoutes(extension, language),
40+
),
41+
);
42+
}
43+
}, [extensions, language]);
3344

3445
useEffect(() => {
3546
if (cluster?.name === currentClusterName) return;
@@ -55,15 +66,17 @@ export default function ClusterRoutes() {
5566

5667
return (
5768
<Routes>
58-
<Route
59-
path="*"
60-
element={
61-
<IncorrectPath
62-
to="overview"
63-
message={t('components.incorrect-path.message.cluster')}
64-
/>
65-
}
66-
/>
69+
{extensibilityRoutes && (
70+
<Route
71+
path="*"
72+
element={
73+
<IncorrectPath
74+
to="overview"
75+
message={t('components.incorrect-path.message.cluster')}
76+
/>
77+
}
78+
/>
79+
)}
6780
{/* overview route should stay static */}
6881
<Route
6982
path="overview"
@@ -75,9 +88,7 @@ export default function ClusterRoutes() {
7588
/>
7689

7790
{/* extensibility routes should go first, so if someone overwrites the default view, the new one should have a higher priority */}
78-
{extensions?.map(extension =>
79-
createExtensibilityRoutes(extension, language),
80-
)}
91+
{extensibilityRoutes}
8192
{resourceRoutes}
8293
{otherRoutes}
8394
<Route path="namespaces/:namespaceId/*" element={<NamespaceRoutes />} />

src/components/App/NamespaceRoutes.tsx

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useEffect, useState } from 'react';
12
import { Routes, Route, useParams } from 'react-router-dom';
23
import { useRecoilValue } from 'recoil';
34
import { useTranslation } from 'react-i18next';
@@ -18,6 +19,19 @@ export default function NamespaceRoutes() {
1819
const { clusterUrl } = useUrl();
1920
const language = useRecoilValue(languageAtom);
2021
const extensions = useRecoilValue(extensionsState);
22+
const [extensibilityRoutes, setExtensibilityRoutes] = useState<
23+
JSX.Element[] | null
24+
>(null);
25+
26+
useEffect(() => {
27+
if (extensions?.length) {
28+
setExtensibilityRoutes(
29+
extensions.map(extension =>
30+
createExtensibilityRoutes(extension, language),
31+
),
32+
);
33+
}
34+
}, [extensions, language]);
2135

2236
const { error } = useGet(
2337
namespaceId === '-all-'
@@ -27,7 +41,7 @@ export default function NamespaceRoutes() {
2741
skip: false,
2842
pollingInterval: 0,
2943
onDataReceived: () => {},
30-
},
44+
} as any,
3145
);
3246
const hasAccessToNamespace =
3347
JSON.parse(JSON.stringify(error)) === null ||
@@ -43,19 +57,19 @@ export default function NamespaceRoutes() {
4357

4458
return (
4559
<Routes>
46-
<Route
47-
path="*"
48-
element={
49-
<IncorrectPath
50-
to=""
51-
message={t('components.incorrect-path.message.namespace')}
52-
/>
53-
}
54-
/>
55-
{/* extensibility routes should go first, so if someone overwrites the default view, the new one should have a higher priority */}
56-
{extensions?.map(extension =>
57-
createExtensibilityRoutes(extension, language),
60+
{extensibilityRoutes && (
61+
<Route
62+
path="*"
63+
element={
64+
<IncorrectPath
65+
to=""
66+
message={t('components.incorrect-path.message.namespace')}
67+
/>
68+
}
69+
/>
5870
)}
71+
{/* extensibility routes should go first, so if someone overwrites the default view, the new one should have a higher priority */}
72+
{extensibilityRoutes}
5973
{resourceRoutesNamespaced}
6074
{otherRoutesNamespaced}
6175
</Routes>

src/shared/components/ListActions/ListActions.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ const StandaloneAction = ({ action, entry }) => {
2222
return (
2323
<Button
2424
data-testid={action.name.replace(' ', '').toLowerCase()}
25-
onClick={() => action.handler(entry)}
25+
onClick={e => {
26+
e.stopPropagation();
27+
action.handler(entry);
28+
}}
2629
className="list-actions__standalone"
2730
design="Transparent"
2831
icon={typeof icon === 'function' ? icon(entry) : icon}

src/sidebar/NavItem.tsx

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
import { useEffect } from 'react';
2+
import {
3+
useLocation,
4+
useNavigate,
5+
useNavigationType,
6+
NavigationType,
7+
} from 'react-router-dom';
18
import { useTranslation } from 'react-i18next';
29
import { NavNode } from 'state/types';
310
import { useUrl } from 'hooks/useUrl';
@@ -11,7 +18,6 @@ import {
1118
SideNavigationSubItem,
1219
SideNavigationItem,
1320
} from '@ui5/webcomponents-react';
14-
import { useNavigate } from 'react-router-dom';
1521
import { isResourceEditedState } from 'state/resourceEditedAtom';
1622

1723
import { isFormOpenState } from 'state/formOpenAtom';
@@ -28,6 +34,8 @@ export function NavItem({ node, subItem = false }: NavItemProps) {
2834
const { t } = useTranslation();
2935
const urlGenerators = useUrl();
3036
const navigate = useNavigate();
37+
const location = useLocation();
38+
const navigationType = useNavigationType();
3139
const setLayoutColumn = useSetRecoilState(columnLayoutState);
3240
const [isResourceEdited, setIsResourceEdited] = useRecoilState(
3341
isResourceEditedState,
@@ -55,44 +63,57 @@ export function NavItem({ node, subItem = false }: NavItemProps) {
5563
}
5664
};
5765

58-
let propsForNav = {
66+
const handleNavigation = (isNavigatingForward?: boolean) => {
67+
if (node.dataSources) {
68+
let link =
69+
!jsonataError && jsonataLink ? jsonataLink : node.externalUrl ?? '';
70+
link = link.startsWith('http') ? link : `https://${link}`;
71+
const newWindow = window.open(link, 'noopener, noreferrer');
72+
if (newWindow) newWindow.opener = null;
73+
} else if (node.externalUrl) {
74+
const link = node.externalUrl.startsWith('http')
75+
? node.externalUrl
76+
: `https://${node.externalUrl}`;
77+
const newWindow = window.open(link, 'noopener, noreferrer');
78+
if (newWindow) newWindow.opener = null;
79+
} else {
80+
handleActionIfFormOpen(
81+
isResourceEdited,
82+
setIsResourceEdited,
83+
isFormOpen,
84+
setIsFormOpen,
85+
() => {
86+
setLayoutColumn({
87+
midColumn: null,
88+
endColumn: null,
89+
layout: 'OneColumn',
90+
});
91+
const url = node.createUrlFn
92+
? node.createUrlFn(urlGenerators)
93+
: scopedUrl(node.pathSegment);
94+
if (location?.pathname !== url && isNavigatingForward) {
95+
navigate(url);
96+
}
97+
},
98+
);
99+
}
100+
};
101+
102+
useEffect(() => {
103+
if (navigationType === NavigationType.Pop) {
104+
handleNavigation();
105+
}
106+
// eslint-disable-next-line react-hooks/exhaustive-deps
107+
}, [navigationType]);
108+
109+
const propsForNav = {
59110
icon: node.externalUrl ? 'action' : node.icon,
60111
text: t(node.label, { defaultValue: node.label }),
61112
selected: isNodeSelected(node),
62113
key: node.pathSegment,
63114
onClick: (e: Event) => {
64-
if (node.dataSources) {
65-
let link =
66-
!jsonataError && jsonataLink ? jsonataLink : node.externalUrl || '';
67-
link = link.startsWith('http') ? link : `https://${link}`;
68-
const newWindow = window.open(link, 'noopener, noreferrer');
69-
if (newWindow) newWindow.opener = null;
70-
} else if (node.externalUrl) {
71-
const link = node.externalUrl.startsWith('http')
72-
? node.externalUrl
73-
: `https://${node.externalUrl}`;
74-
const newWindow = window.open(link, 'noopener, noreferrer');
75-
if (newWindow) newWindow.opener = null;
76-
} else {
77-
handleActionIfFormOpen(
78-
isResourceEdited,
79-
setIsResourceEdited,
80-
isFormOpen,
81-
setIsFormOpen,
82-
() => {
83-
setLayoutColumn({
84-
midColumn: null,
85-
endColumn: null,
86-
layout: 'OneColumn',
87-
});
88-
navigate(
89-
node.createUrlFn
90-
? node.createUrlFn(urlGenerators)
91-
: scopedUrl(node.pathSegment),
92-
);
93-
},
94-
);
95-
}
115+
e.stopPropagation();
116+
handleNavigation(true);
96117
},
97118
};
98119

0 commit comments

Comments
 (0)