Skip to content

Commit 1dc819b

Browse files
authored
refactor: FormOpen state and isResourceEdit state (#3866)
* refactor: formOpen state and isResourceEditState * fix: minor bug fix * test: test adjustment to changes * fix: isEdit should return false if initialResource or resource is null * some cleanup and test adjustments * some cleanup and test adjustments * some cleanup and test adjustments
1 parent 9614d14 commit 1dc819b

File tree

17 files changed

+258
-420
lines changed

17 files changed

+258
-420
lines changed

src/command-pallette/CommandPalletteUI/CommandPaletteUI.tsx

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ReactNode, useEffect, useRef, useState } from 'react';
2-
import { useRecoilState, useRecoilValue } from 'recoil';
2+
import { useRecoilValue } from 'recoil';
33
import { useEventListener } from 'hooks/useEventListener';
44
import { addHistoryEntry, getHistoryEntries } from './search-history';
55
import { activeNamespaceIdState } from 'state/activeNamespaceIdAtom';
@@ -14,11 +14,9 @@ import { useSearchResults } from './useSearchResults';
1414
import { K8sResource } from 'types';
1515
import { Button, Icon, Input } from '@ui5/webcomponents-react';
1616
import './CommandPaletteUI.scss';
17-
import { handleActionIfFormOpen } from 'shared/components/UnsavedMessageBox/helpers';
18-
import { isResourceEditedState } from 'state/resourceEditedAtom';
1917
import { showKymaCompanionState } from 'state/companion/showKymaCompanionAtom';
20-
import { isFormOpenState } from 'state/formOpenAtom';
2118
import { SCREEN_SIZE_BREAKPOINT_M } from './types';
19+
import { useFormNavigation } from 'shared/hooks/useFormNavigation';
2220

2321
function Background({
2422
hide,
@@ -58,10 +56,7 @@ export function CommandPaletteUI({
5856
shellbarWidth,
5957
}: CommandPaletteProps) {
6058
const namespace = useRecoilValue(activeNamespaceIdState);
61-
const [isResourceEdited, setIsResourceEdited] = useRecoilState(
62-
isResourceEditedState,
63-
);
64-
const [isFormOpen, setIsFormOpen] = useRecoilState(isFormOpenState);
59+
const { navigateSafely } = useFormNavigation();
6560

6661
const [query, setQuery] = useState('');
6762
const [originalQuery, setOriginalQuery] = useState('');
@@ -153,17 +148,10 @@ export function CommandPaletteUI({
153148
if (key === 'Enter' && results[0]) {
154149
// choose current entry
155150
e.preventDefault();
156-
157-
handleActionIfFormOpen(
158-
isResourceEdited,
159-
setIsResourceEdited,
160-
isFormOpen,
161-
setIsFormOpen,
162-
() => {
163-
addHistoryEntry(results[0].query);
164-
results[0].onActivate();
165-
},
166-
);
151+
navigateSafely(() => {
152+
addHistoryEntry(results[0].query);
153+
results[0].onActivate();
154+
});
167155
} else if (key === 'Tab') {
168156
e.preventDefault();
169157
// fill search with active history entry

src/command-pallette/CommandPalletteUI/ResultsList/ResultsList.tsx

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ import { addHistoryEntry } from '../search-history';
66
import './ResultsList.scss';
77
import { useTranslation } from 'react-i18next';
88
import { LOADING_INDICATOR } from '../types';
9-
import { useRecoilState } from 'recoil';
10-
import { isResourceEditedState } from 'state/resourceEditedAtom';
11-
import { isFormOpenState } from 'state/formOpenAtom';
12-
import { handleActionIfFormOpen } from 'shared/components/UnsavedMessageBox/helpers';
9+
import { useFormNavigation } from 'shared/hooks/useFormNavigation';
1310

1411
function scrollInto(element: Element) {
1512
element.scrollIntoView({
@@ -36,10 +33,7 @@ export function ResultsList({
3633
}: ResultsListProps) {
3734
const listRef = useRef<HTMLUListElement | null>(null);
3835
const { t } = useTranslation();
39-
const [isResourceEdited, setIsResourceEdited] = useRecoilState(
40-
isResourceEditedState,
41-
);
42-
const [isFormOpen, setIsFormOpen] = useRecoilState(isFormOpenState);
36+
const { navigateSafely } = useFormNavigation();
4337

4438
//todo 2
4539
const isLoading = results.find((r: any) => r.type === LOADING_INDICATOR);
@@ -71,16 +65,10 @@ export function ResultsList({
7165
scrollInto(listRef.current!.children[activeIndex - 1]);
7266
} else if (key === 'Enter' && results?.[activeIndex]) {
7367
e.preventDefault();
74-
handleActionIfFormOpen(
75-
isResourceEdited,
76-
setIsResourceEdited,
77-
isFormOpen,
78-
setIsFormOpen,
79-
() => {
80-
addHistoryEntry(results[activeIndex].query);
81-
results[activeIndex].onActivate();
82-
},
83-
);
68+
navigateSafely(() => {
69+
addHistoryEntry(results[activeIndex].query);
70+
results[activeIndex].onActivate();
71+
});
8472
}
8573
},
8674
[activeIndex, results, isHistoryMode],
@@ -97,16 +85,10 @@ export function ResultsList({
9785
activeIndex={activeIndex}
9886
setActiveIndex={setActiveIndex}
9987
onItemClick={() => {
100-
handleActionIfFormOpen(
101-
isResourceEdited,
102-
setIsResourceEdited,
103-
isFormOpen,
104-
setIsFormOpen,
105-
() => {
106-
addHistoryEntry(result.query);
107-
result.onActivate();
108-
},
109-
);
88+
navigateSafely(() => {
89+
addHistoryEntry(result.query);
90+
result.onActivate();
91+
});
11092
}}
11193
/>
11294
))

src/header/Header.tsx

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useRef, useState } from 'react';
2-
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
2+
import { useRecoilValue, useSetRecoilState } from 'recoil';
33
import {
44
Avatar,
55
ShellBar,
@@ -9,23 +9,21 @@ import {
99

1010
import { useTranslation } from 'react-i18next';
1111
import { useNavigate } from 'react-router';
12+
import { useFormNavigation } from 'shared/hooks/useFormNavigation';
1213
import { useFeature } from 'hooks/useFeature';
1314
import { useAvailableNamespaces } from 'hooks/useAvailableNamespaces';
1415
import { useCheckSAPUser } from 'hooks/useCheckSAPUser';
1516

1617
import { clustersState } from 'state/clustersAtom';
1718
import { clusterState } from 'state/clusterAtom';
1819
import { showKymaCompanionState } from 'state/companion/showKymaCompanionAtom';
19-
import { isResourceEditedState } from 'state/resourceEditedAtom';
20-
import { isFormOpenState } from 'state/formOpenAtom';
2120

2221
import { Logo } from './Logo/Logo';
2322
import { SidebarSwitcher } from './SidebarSwitcher/SidebarSwitcher';
2423
import { HeaderMenu } from './HeaderMenu';
2524
import { CommandPaletteSearchBar } from 'command-pallette/CommandPalletteUI/CommandPaletteSearchBar';
2625
import { SnowFeature } from './SnowFeature';
2726

28-
import { handleActionIfFormOpen } from 'shared/components/UnsavedMessageBox/helpers';
2927
import { configFeaturesNames } from 'state/types';
3028
import './Header.scss';
3129

@@ -38,16 +36,13 @@ export function Header() {
3836

3937
const { t } = useTranslation();
4038
const navigate = useNavigate();
39+
const { navigateSafely } = useFormNavigation();
4140
const { isEnabled: isFeedbackEnabled, link: feedbackLink } = useFeature(
4241
configFeaturesNames.FEEDBACK,
4342
);
4443

4544
const cluster = useRecoilValue(clusterState);
4645
const clusters = useRecoilValue(clustersState);
47-
const [isResourceEdited, setIsResourceEdited] = useRecoilState(
48-
isResourceEditedState,
49-
);
50-
const [isFormOpen, setIsFormOpen] = useRecoilState(isFormOpenState);
5146

5247
const { isEnabled: isKymaCompanionEnabled } = useFeature('KYMA_COMPANION');
5348
const setShowCompanion = useSetRecoilState(showKymaCompanionState);
@@ -83,13 +78,7 @@ export function Header() {
8378
window.location.pathname !== '/clusters' && <SidebarSwitcher />
8479
}
8580
onLogoClick={() => {
86-
handleActionIfFormOpen(
87-
isResourceEdited,
88-
setIsResourceEdited,
89-
isFormOpen,
90-
setIsFormOpen,
91-
() => navigate('/clusters'),
92-
);
81+
navigateSafely(() => navigate('/clusters'));
9382
setShowCompanion({
9483
show: false,
9584
fullScreen: false,
@@ -103,22 +92,16 @@ export function Header() {
10392
}
10493
menuItems={window.location.pathname !== '/clusters' ? clustersList : []}
10594
onMenuItemClick={e => {
106-
handleActionIfFormOpen(
107-
isResourceEdited,
108-
setIsResourceEdited,
109-
isFormOpen,
110-
setIsFormOpen,
111-
() => {
112-
e.detail.item.textContent ===
113-
t('clusters.overview.title-all-clusters')
114-
? navigate('/clusters')
115-
: navigate(
116-
`/cluster/${encodeURIComponent(
117-
e.detail.item?.textContent ?? '',
118-
)}`,
119-
);
120-
},
121-
);
95+
navigateSafely(() => {
96+
e.detail.item.textContent ===
97+
t('clusters.overview.title-all-clusters')
98+
? navigate('/clusters')
99+
: navigate(
100+
`/cluster/${encodeURIComponent(
101+
e.detail.item?.textContent ?? '',
102+
)}`,
103+
);
104+
});
122105
setShowCompanion({
123106
show: false,
124107
fullScreen: false,

src/header/NamespaceChooser/NamespaceChooser.tsx

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1-
import { useRecoilState, useRecoilValue } from 'recoil';
1+
import { useRecoilValue } from 'recoil';
22
import { useTranslation } from 'react-i18next';
33
import { useUrl } from 'hooks/useUrl';
44
import { useMatch, useNavigate } from 'react-router';
55
import { namespacesState } from 'state/namespacesAtom';
66

77
import { SideNavigationSubItem } from '@ui5/webcomponents-react';
8-
import { isResourceEditedState } from 'state/resourceEditedAtom';
9-
import { isFormOpenState } from 'state/formOpenAtom';
10-
import { handleActionIfFormOpen } from 'shared/components/UnsavedMessageBox/helpers';
8+
import { useFormNavigation } from 'shared/hooks/useFormNavigation';
119

1210
export function NamespaceChooser() {
1311
const { t } = useTranslation();
1412
const navigate = useNavigate();
1513
const { namespaceUrl } = useUrl();
1614
const allNamespaces = useRecoilValue(namespacesState);
17-
const [isResourceEdited, setIsResourceEdited] = useRecoilState(
18-
isResourceEditedState,
19-
);
20-
const [isFormOpen, setIsFormOpen] = useRecoilState(isFormOpenState);
15+
const { navigateSafely } = useFormNavigation();
2116

2217
const { resourceType = '' } =
2318
useMatch({
@@ -31,12 +26,8 @@ export function NamespaceChooser() {
3126
text={t('navigation.all-namespaces')}
3227
data-key="all-namespaces"
3328
onClick={() => {
34-
handleActionIfFormOpen(
35-
isResourceEdited,
36-
setIsResourceEdited,
37-
isFormOpen,
38-
setIsFormOpen,
39-
() => navigate(namespaceUrl(resourceType, { namespace: '-all-' })),
29+
navigateSafely(() =>
30+
navigate(namespaceUrl(resourceType, { namespace: '-all-' })),
4031
);
4132
}}
4233
/>,
@@ -49,17 +40,12 @@ export function NamespaceChooser() {
4940
key={ns}
5041
data-key={ns}
5142
onClick={e => {
52-
handleActionIfFormOpen(
53-
isResourceEdited,
54-
setIsResourceEdited,
55-
isFormOpen,
56-
setIsFormOpen,
57-
() =>
58-
navigate(
59-
namespaceUrl(resourceType, {
60-
namespace: e.target.dataset.key ?? undefined,
61-
}),
62-
),
43+
navigateSafely(() =>
44+
navigate(
45+
namespaceUrl(resourceType, {
46+
namespace: e.target.dataset.key ?? undefined,
47+
}),
48+
),
6349
);
6450
}}
6551
/>,

src/shared/ResourceForm/components/ResourceForm.js

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,15 @@ import jp from 'jsonpath';
1414
import { Form, FormItem } from '@ui5/webcomponents-react';
1515
import { UI5Panel } from 'shared/components/UI5Panel/UI5Panel';
1616

17-
import { useRecoilState, useRecoilValue } from 'recoil';
17+
import { useRecoilValue } from 'recoil';
1818
import { editViewModeState } from 'state/preferences/editViewModeAtom';
19-
import { isResourceEditedState } from 'state/resourceEditedAtom';
20-
import { isFormOpenState } from 'state/formOpenAtom';
2119
import { createPortal } from 'react-dom';
2220
import { UnsavedMessageBox } from 'shared/components/UnsavedMessageBox/UnsavedMessageBox';
23-
import { cloneDeep } from 'lodash';
2421
import { getDescription, SchemaContext } from 'shared/helpers/schema';
2522

26-
import './ResourceForm.scss';
2723
import { columnLayoutState } from 'state/columnLayoutAtom';
28-
29-
export const excludeStatus = resource => {
30-
const modifiedResource = cloneDeep(resource);
31-
delete modifiedResource.status;
32-
delete modifiedResource.metadata?.resourceVersion;
33-
delete modifiedResource.metadata?.managedFields;
34-
return modifiedResource;
35-
};
24+
import { useFormEditTracking } from 'shared/hooks/useFormEditTracking';
25+
import './ResourceForm.scss';
3626

3727
export function ResourceForm({
3828
pluralKind, // used for the request path
@@ -101,37 +91,9 @@ export function ResourceForm({
10191
}
10292

10393
const editViewMode = useRecoilValue(editViewModeState);
104-
const [isResourceEdited, setIsResourceEdited] = useRecoilState(
105-
isResourceEditedState,
106-
);
107-
const [isFormOpen, setIsFormOpen] = useRecoilState(isFormOpenState);
108-
const { leavingForm } = isFormOpen;
10994
const [editorError, setEditorError] = useState(null);
11095

111-
useEffect(() => {
112-
// Check if form is opened based on width
113-
if (leavingForm && formElementRef?.current?.clientWidth !== 0) {
114-
if (
115-
JSON.stringify(excludeStatus(resource)) !==
116-
JSON.stringify(excludeStatus(initialResource)) ||
117-
editorError
118-
) {
119-
setIsResourceEdited({ ...isResourceEdited, isEdited: true });
120-
}
121-
122-
if (
123-
JSON.stringify(excludeStatus(resource)) ===
124-
JSON.stringify(excludeStatus(initialResource)) &&
125-
!editorError
126-
) {
127-
setIsResourceEdited({ isEdited: false });
128-
setIsFormOpen({ formOpen: false });
129-
if (isResourceEdited.discardAction) isResourceEdited.discardAction();
130-
}
131-
}
132-
133-
// eslint-disable-next-line react-hooks/exhaustive-deps
134-
}, [leavingForm]);
96+
useFormEditTracking(resource, initialResource, editorError);
13597

13698
const { t } = useTranslation();
13799
const createResource = useCreateResource({

0 commit comments

Comments
 (0)