Skip to content

Commit 35d1ad6

Browse files
AAJELLALayolab
andauthored
Add the link to the modifications and nodes in the root network search. (#3266)
Signed-off-by: AAJELLAL <[email protected]> Co-authored-by: Ayoub LABIDI <[email protected]>
1 parent 3178b59 commit 35d1ad6

16 files changed

+200
-49
lines changed

src/components/app-wrapper.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ const lightTheme = createTheme({
199199
overlay: {
200200
background: '#e6e6e6',
201201
},
202+
highlightColor: '#1976D214',
202203
},
203204
networkModificationPanel: {
204205
backgroundColor: 'white',
@@ -311,6 +312,7 @@ const darkTheme = createTheme({
311312
overlay: {
312313
background: '#121212',
313314
},
315+
highlightColor: '#90CAF929',
314316
},
315317
networkModificationPanel: {
316318
backgroundColor: '#252525',

src/components/graph/menus/network-modifications/network-modifications-table.tsx

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
77

8-
import React, { SetStateAction, useCallback, useMemo } from 'react';
8+
import React, { useCallback, useEffect, useMemo, SetStateAction, useRef } from 'react';
99
import {
1010
CustomAGGrid,
1111
type MuiStyles,
@@ -25,7 +25,7 @@ import type {
2525
ValueGetterParams,
2626
} from 'ag-grid-community';
2727
import { RemoveRedEye as RemoveRedEyeIcon } from '@mui/icons-material';
28-
import { Badge, Box } from '@mui/material';
28+
import { Badge, Box, useTheme } from '@mui/material';
2929
import { useSelector } from 'react-redux';
3030
import { AppState } from 'redux/reducer';
3131
import { useIntl } from 'react-intl';
@@ -38,6 +38,7 @@ import SwitchCellRenderer from './switch-cell-renderer';
3838
import { AGGRID_LOCALES } from '../../../../translations/not-intl/aggrid-locales';
3939
import { ExcludedNetworkModifications } from './network-modification-menu.type';
4040
import { NetworkModificationNameCellRenderer } from 'components/custom-aggrid/cell-renderers';
41+
import { AgGridReact } from 'ag-grid-react';
4142

4243
const styles = {
4344
container: (theme) => ({
@@ -83,8 +84,11 @@ const NetworkModificationsTable: React.FC<NetworkModificationsTableProps> = ({
8384
setModificationsToExclude,
8485
...nameHeaderProps
8586
}) => {
87+
const gridRef = useRef<AgGridReact>(null);
88+
const theme = useTheme();
8689
const rootNetworks = useSelector((state: AppState) => state.rootNetworks);
8790
const isMonoRootStudy = useSelector((state: AppState) => state.isMonoRootStudy);
91+
const highlightedModificationUuid = useSelector((state: AppState) => state.highlightedModificationUuid);
8892

8993
const intl = useIntl();
9094
const { computeLabel } = useModificationLabelComputer();
@@ -199,17 +203,37 @@ const NetworkModificationsTable: React.FC<NetworkModificationsTableProps> = ({
199203

200204
const getRowId = (params: GetRowIdParams<NetworkModificationMetadata>) => params.data.uuid;
201205

202-
const getRowStyle = useCallback((cellData: RowClassParams<NetworkModificationMetadata, unknown>) => {
203-
const style: RowStyle = {};
204-
if (!cellData?.data?.activated) {
205-
style.opacity = 0.4;
206+
const getRowStyle = useCallback(
207+
(cellData: RowClassParams<NetworkModificationMetadata, unknown>) => {
208+
const style: RowStyle = {};
209+
if (!cellData?.data?.activated) {
210+
style.opacity = 0.4;
211+
}
212+
if (cellData?.data?.uuid === highlightedModificationUuid && cellData?.rowIndex !== null) {
213+
style.backgroundColor = theme.aggrid.highlightColor;
214+
}
215+
return style;
216+
},
217+
[highlightedModificationUuid, theme]
218+
);
219+
220+
const handleScroll = useCallback(() => {
221+
if (highlightedModificationUuid && gridRef.current?.api) {
222+
const selectedRow = gridRef.current.api.getRowNode(highlightedModificationUuid);
223+
if (selectedRow) {
224+
gridRef.current.api.ensureNodeVisible(selectedRow, 'top');
225+
}
206226
}
207-
return style;
208-
}, []);
227+
}, [highlightedModificationUuid]);
228+
229+
useEffect(() => {
230+
handleScroll();
231+
}, [handleScroll, highlightedModificationUuid]);
209232

210233
return (
211234
<Box sx={styles.container}>
212235
<CustomAGGrid
236+
ref={gridRef}
213237
rowData={modifications}
214238
getRowId={getRowId}
215239
rowSelection={{
@@ -226,6 +250,7 @@ const NetworkModificationsTable: React.FC<NetworkModificationsTableProps> = ({
226250
getRowStyle={getRowStyle}
227251
onRowDragEnter={onRowDragStart}
228252
onRowDragEnd={onRowDragEnd}
253+
onFirstDataRendered={handleScroll}
229254
rowDragManaged={!isRowDragDisabled}
230255
suppressNoRowsOverlay={true}
231256
overrideLocales={AGGRID_LOCALES}

src/components/graph/menus/network-modifications/node-editor.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import NetworkModificationNodeEditor from './network-modification-node-editor';
99
import { ComputingType, type MuiStyles } from '@gridsuite/commons-ui';
1010
import { useDispatch, useSelector } from 'react-redux';
11-
import { setToggleOptions } from '../../../../redux/actions';
11+
import { setHighlightModification, setToggleOptions } from '../../../../redux/actions';
1212
import { Box } from '@mui/material';
1313
import { AppState } from '../../../../redux/reducer';
1414
import RunningStatus from 'components/utils/running-status';
@@ -35,6 +35,7 @@ const NodeEditor = () => {
3535
const toggleOptions = useSelector((state: AppState) => state.toggleOptions);
3636

3737
const closeModificationsDrawer = () => {
38+
dispatch(setHighlightModification(null));
3839
dispatch(setToggleOptions(toggleOptions.filter((option) => option !== StudyDisplayMode.MODIFICATIONS)));
3940
};
4041

src/components/graph/menus/root-network/root-network-modification-results.tsx

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,41 @@ import { useIntl } from 'react-intl';
88
import { useModificationLabelComputer } from '@gridsuite/commons-ui';
99
import { useCallback } from 'react';
1010
import { Modification } from './root-network.types';
11-
import { Typography } from '@mui/material';
11+
import { Box, Theme, Typography } from '@mui/material';
12+
import { UUID } from 'crypto';
13+
import { AppState } from 'redux/reducer';
14+
import { useDispatch, useSelector } from 'react-redux';
15+
import { setHighlightModification, setModificationsDrawerOpen } from 'redux/actions';
16+
import { useSyncNavigationActions } from 'hooks/use-sync-navigation-actions';
17+
import { useTreeNodeFocus } from 'hooks/use-tree-node-focus';
1218

1319
interface ModificationResultsProps {
1420
modifications: Modification[];
21+
nodeUuid: UUID;
1522
}
1623

17-
export const ModificationResults: React.FC<ModificationResultsProps> = ({ modifications }) => {
24+
const styles = {
25+
itemHover: (theme: Theme) => ({
26+
borderRadius: 1,
27+
cursor: 'pointer',
28+
'&:hover': {
29+
backgroundColor: theme.aggrid.highlightColor,
30+
},
31+
}),
32+
modificationLabel: {
33+
cursor: 'pointer',
34+
pt: 0.5,
35+
pb: 0.5,
36+
pl: 0.5,
37+
},
38+
};
39+
export const ModificationResults: React.FC<ModificationResultsProps> = ({ modifications, nodeUuid }) => {
1840
const intl = useIntl();
1941
const { computeLabel } = useModificationLabelComputer();
42+
const treeNodes = useSelector((state: AppState) => state.networkModificationTreeModel?.treeNodes);
43+
const triggerTreeNodeFocus = useTreeNodeFocus();
44+
const { setCurrentTreeNodeWithSync } = useSyncNavigationActions();
45+
const dispatch = useDispatch();
2046

2147
const getModificationLabel = useCallback(
2248
(modification?: Modification): React.ReactNode => {
@@ -28,18 +54,34 @@ export const ModificationResults: React.FC<ModificationResultsProps> = ({ modifi
2854
{ id: 'network_modifications.' + modification.messageType },
2955
{
3056
// @ts-ignore
31-
...computeLabel(modification),
57+
...computeLabel(modification, false),
3258
}
3359
);
3460
},
3561
[computeLabel, intl]
3662
);
63+
64+
const handleClick = useCallback(
65+
(modification: Modification) => {
66+
const node = treeNodes?.find((node) => node.id === nodeUuid);
67+
if (node) {
68+
setCurrentTreeNodeWithSync(node);
69+
triggerTreeNodeFocus();
70+
}
71+
dispatch(setModificationsDrawerOpen());
72+
dispatch(setHighlightModification(modification.modificationUuid));
73+
},
74+
[dispatch, nodeUuid, setCurrentTreeNodeWithSync, treeNodes, triggerTreeNodeFocus]
75+
);
76+
3777
return (
3878
<>
3979
{modifications.map((modification) => (
40-
<Typography key={modification.impactedEquipmentId + modification.modificationUuid} variant="body2">
41-
<strong>{modification.impactedEquipmentId + ' - '}</strong> {getModificationLabel(modification)}
42-
</Typography>
80+
<Box sx={styles.itemHover} key={modification.impactedEquipmentId + modification.modificationUuid}>
81+
<Typography variant="body2" onClick={() => handleClick(modification)} sx={styles.modificationLabel}>
82+
<strong>{modification.impactedEquipmentId + ' - '}</strong> {getModificationLabel(modification)}
83+
</Typography>
84+
</Box>
4385
))}
4486
</>
4587
);

src/components/graph/menus/root-network/root-network-modifications-search-results.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export const RootNetworkModificationsSearchResults: React.FC<RootNetworkModifica
6060
<DeviceHubIcon style={styles.iconMinSize} />
6161
<OverflowableText text={getName(result.nodeUuid)} sx={styles.text} maxLineCount={1} />
6262
</Box>
63-
<ModificationResults modifications={result.modifications} />
63+
<ModificationResults modifications={result.modifications} nodeUuid={result.nodeUuid} />
6464
<Divider sx={{ mt: 2 }} />
6565
</Box>
6666
))}

src/components/graph/menus/root-network/root-network-nodes-search-results.tsx

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
* License, v. 2.0. If a copy of the MPL was not distributed with this
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
7+
import { Box, Divider, Theme } from '@mui/material';
8+
import { useCallback } from 'react';
9+
import { useSelector } from 'react-redux';
10+
import { AppState } from 'redux/reducer';
711
import { DeviceHubIcon, type MuiStyles, OverflowableText } from '@gridsuite/commons-ui';
8-
import { Box, Divider } from '@mui/material';
9-
import React from 'react';
12+
import { useSyncNavigationActions } from 'hooks/use-sync-navigation-actions';
13+
import { useTreeNodeFocus } from 'hooks/use-tree-node-focus';
1014

1115
interface RootNetworkNodesSearchResultsProps {
1216
results: string[];
@@ -20,24 +24,49 @@ const styles = {
2024
rootNameTitle: {
2125
display: 'flex',
2226
alignItems: 'center',
27+
pt: 1,
28+
pl: 0.5,
2329
mb: 1,
2430
},
31+
itemHover: (theme: Theme) => ({
32+
mb: 1,
33+
borderRadius: 1,
34+
cursor: 'pointer',
35+
'&:hover': {
36+
backgroundColor: theme.aggrid.highlightColor,
37+
},
38+
}),
2539
iconMinSize: {
2640
minHeight: '20px',
2741
minWidth: '20px',
2842
},
2943
} as const satisfies MuiStyles;
3044

3145
export const RootNetworkNodesSearchResults: React.FC<RootNetworkNodesSearchResultsProps> = ({ results }) => {
46+
const treeNodes = useSelector((state: AppState) => state.networkModificationTreeModel?.treeNodes);
47+
const { setCurrentTreeNodeWithSync } = useSyncNavigationActions();
48+
const triggerTreeNodeFocus = useTreeNodeFocus();
49+
50+
const handleClick = useCallback(
51+
(nodeName: string) => {
52+
const node = treeNodes?.find((node) => node.data.label === nodeName);
53+
if (node) {
54+
setCurrentTreeNodeWithSync(node);
55+
triggerTreeNodeFocus();
56+
}
57+
},
58+
[setCurrentTreeNodeWithSync, treeNodes, triggerTreeNodeFocus]
59+
);
60+
3261
return (
3362
<Box sx={styles.container}>
3463
{results.map((result) => (
35-
<Box key={result + '_node'} sx={{ mb: 2 }}>
36-
<Box sx={styles.rootNameTitle}>
64+
<Box key={result + '_node'} sx={styles.itemHover}>
65+
<Box sx={styles.rootNameTitle} onClick={() => handleClick(result)}>
3766
<DeviceHubIcon style={styles.iconMinSize} />
3867
<OverflowableText text={result} sx={{ marginLeft: '5px' }} maxLineCount={1} />
3968
</Box>
40-
<Divider sx={{ mt: 2 }} />
69+
<Divider />
4170
</Box>
4271
))}
4372
</Box>

src/components/graph/menus/root-network/root-network-panel-header.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { UUID } from 'crypto';
2525
import { getCaseImportParameters, GetCaseImportParametersReturn } from 'services/network-conversion';
2626
import { customizeCurrentParameters, formatCaseImportParameters } from '../../util/case-import-parameters';
2727
import { useDispatch, useSelector } from 'react-redux';
28-
import { setMonoRootStudy } from 'redux/actions';
28+
import { setHighlightModification, setMonoRootStudy } from 'redux/actions';
2929
import { CustomDialog } from 'components/utils/custom-dialog';
3030
import SearchIcon from '@mui/icons-material/Search';
3131

@@ -191,7 +191,8 @@ const RootNetworkPanelHeader: React.FC<RootNetworkPanelHeaderProps> = ({
191191
const minimizeRootNetworkPanel = useCallback(() => {
192192
setIsSearchActive(false);
193193
setIsRootNetworkPanelMinimized((prev) => !prev);
194-
}, [setIsRootNetworkPanelMinimized, setIsSearchActive]);
194+
dispatch(setHighlightModification(null));
195+
}, [dispatch, setIsRootNetworkPanelMinimized, setIsSearchActive]);
195196

196197
const openSearch = useCallback(() => {
197198
setIsSearchActive(true);

src/components/graph/menus/root-network/root-network-panel-search.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import SearchBar from './root-network-search-bar';
1717
import { RootNetworkNodesSearchResults } from './root-network-nodes-search-results';
1818
import { useRootNetworkNodeSearch } from './use-root-network-node-search';
1919
import { useRootNetworkModificationSearch } from './use-root-network-modification-search';
20+
import { setHighlightModification } from 'redux/actions';
21+
import { useDispatch, useSelector } from 'react-redux';
22+
import { AppState } from '../../../../redux/reducer';
2023

2124
enum TAB_VALUES {
2225
modifications = 'MODIFICATIONS',
@@ -68,21 +71,27 @@ const RootNetworkSearchPanel: React.FC<RootNetworkSearchPanelProps> = ({ setIsSe
6871

6972
const nodesSearch = useRootNetworkNodeSearch();
7073
const modificationsSearch = useRootNetworkModificationSearch();
74+
const highlightedModificationUuid = useSelector((state: AppState) => state.highlightedModificationUuid);
7175

7276
const isLoading = isNodeTab(tabValue) ? nodesSearch.isLoading : modificationsSearch.isLoading;
7377
const searchTerm = isNodeTab(tabValue) ? nodesSearch.searchTerm : modificationsSearch.searchTerm;
74-
78+
const dispatch = useDispatch();
7579
const leaveSearch = () => {
7680
nodesSearch.reset();
7781
modificationsSearch.reset();
7882
setIsSearchActive(false);
83+
dispatch(setHighlightModification(null));
7984
};
8085

8186
const handleOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
8287
const value = e.target.value;
8388
if (isNodeTab(tabValue)) {
8489
nodesSearch.search(value);
8590
} else {
91+
// remove the highlighted Modification before search
92+
if (highlightedModificationUuid) {
93+
dispatch(setHighlightModification(null));
94+
}
8695
modificationsSearch.search(value);
8796
}
8897
};

src/components/network-modification-tree-pane.jsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const noNodeSelectionForCopy = {
5454

5555
export const HTTP_MAX_NODE_BUILDS_EXCEEDED_MESSAGE = 'MAX_NODE_BUILDS_EXCEEDED';
5656

57-
export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid, onTreePanelResize }) => {
57+
export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid }) => {
5858
const dispatch = useDispatch();
5959
const { snackError, snackWarning, snackInfo } = useSnackMessage();
6060
const DownloadIframe = 'downloadIframe';
@@ -555,11 +555,7 @@ export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid,
555555

556556
return (
557557
<>
558-
<NetworkModificationTree
559-
onNodeContextMenu={onNodeContextMenu}
560-
studyUuid={studyUuid}
561-
onTreePanelResize={onTreePanelResize}
562-
/>
558+
<NetworkModificationTree onNodeContextMenu={onNodeContextMenu} studyUuid={studyUuid} />
563559
{createNodeMenu.display && (
564560
<CreateNodeMenu
565561
position={createNodeMenu.position}
@@ -611,5 +607,4 @@ export default NetworkModificationTreePane;
611607
NetworkModificationTreePane.propTypes = {
612608
studyUuid: PropTypes.string.isRequired,
613609
currentRootNetworkUuid: PropTypes.string.isRequired,
614-
onTreePanelResize: PropTypes.object.isRequired,
615610
};

0 commit comments

Comments
 (0)