Skip to content

Commit 9dc89b3

Browse files
authored
show tooltip when hovering node (#3294)
* show tooltip when hovering node --------- Signed-off-by: SOUISSI Maissa (Externe) <[email protected]>
1 parent 5ce2d3d commit 9dc89b3

File tree

4 files changed

+148
-72
lines changed

4 files changed

+148
-72
lines changed

src/components/graph/nodes/network-modification-node.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { NodeProps, Position } from '@xyflow/react';
99
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
1010
import { useSelector } from 'react-redux';
1111
import Box from '@mui/material/Box';
12-
import { LIGHT_THEME, type MuiStyles, OverflowableText } from '@gridsuite/commons-ui';
12+
import { LIGHT_THEME, type MuiStyles } from '@gridsuite/commons-ui';
1313
import { getLocalStorageTheme } from '../../../redux/session-storage/local-storage';
1414
import { BUILD_STATUS } from '../../network/constants';
1515
import { AppState } from 'redux/reducer';
@@ -21,6 +21,7 @@ import NodeOverlaySpinner from './node-overlay-spinner';
2121
import BuildStatusChip from './build-status-chip';
2222
import React from 'react';
2323
import { BuildButton } from './build-button';
24+
import { Typography } from '@mui/material';
2425

2526
const styles = {
2627
networkModificationSelected: (theme) => ({
@@ -43,12 +44,19 @@ const styles = {
4344
marginRight: theme.spacing(1),
4445
marginBottom: theme.spacing(1),
4546
}),
46-
overflowText: (theme) => ({
47+
typographyText: (theme) => ({
4748
color: theme.palette.text.primary,
4849
fontSize: '20px',
4950
fontWeight: 400,
5051
lineHeight: 'normal',
5152
textAlign: 'left',
53+
display: '-webkit-box',
54+
WebkitBoxOrient: 'vertical',
55+
WebkitLineClamp: 2,
56+
overflow: 'hidden',
57+
width: 'auto',
58+
textOverflow: 'ellipsis',
59+
wordBreak: 'break-word',
5260
}),
5361
footerBox: (theme) => ({
5462
display: 'flex',
@@ -118,12 +126,9 @@ const NetworkModificationNode = (props: NodeProps<ModificationNode>) => {
118126
]}
119127
>
120128
<Box sx={styles.contentBox}>
121-
<OverflowableText
122-
text={props.data.label}
123-
sx={styles.overflowText}
124-
tooltipSx={styles.tooltip}
125-
maxLineCount={2}
126-
/>
129+
<Typography variant="body1" sx={styles.typographyText}>
130+
{props.data.label}
131+
</Typography>
127132
</Box>
128133

129134
<Box sx={styles.footerBox}>

src/components/network-modification-tree.jsx

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

8-
import { Box } from '@mui/material';
8+
import { Box, Tooltip } from '@mui/material';
99
import { Controls, ReactFlow, useEdgesState, useNodesState, useReactFlow } from '@xyflow/react';
1010
import CenterFocusIcon from '@mui/icons-material/CenterFocusStrong';
11-
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
11+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
1212
import { reorderNetworkModificationTreeNodes, setModificationsDrawerOpen, setToggleOptions } from '../redux/actions';
1313
import { useDispatch, useSelector } from 'react-redux';
14-
import { isSameNode } from './graph/util/model-functions';
14+
import { isRootNode, isSameNode } from './graph/util/model-functions';
1515
import PropTypes from 'prop-types';
1616
import CropFreeIcon from '@mui/icons-material/CropFree';
1717
import { nodeTypes } from './graph/util/model-constants';
@@ -31,21 +31,32 @@ import { groupIdSuffix } from './graph/nodes/labeled-group-node.type';
3131
import { StudyDisplayMode } from './network-modification.type';
3232
import { useSyncNavigationActions } from 'hooks/use-sync-navigation-actions';
3333
import { NodeType } from './graph/tree-node.type';
34-
35-
const styles = (theme) => ({
36-
flexGrow: 1,
37-
height: '100%',
38-
backgroundColor: theme.reactflow.backgroundColor,
39-
'.react-flow': {
40-
'--xy-edge-stroke': theme.reactflow.edge.stroke,
41-
},
42-
'.react-flow__attribution a': {
43-
color: theme.palette.text.primary,
44-
},
45-
'.react-flow__attribution': {
46-
backgroundColor: theme.palette.background.paper,
47-
},
48-
});
34+
import { useIntl } from 'react-intl';
35+
36+
const styles = {
37+
modificationTree: (theme) => ({
38+
flexGrow: 1,
39+
height: '100%',
40+
backgroundColor: theme.reactflow.backgroundColor,
41+
'.react-flow': {
42+
'--xy-edge-stroke': theme.reactflow.edge.stroke,
43+
},
44+
'.react-flow__attribution a': {
45+
color: theme.palette.text.primary,
46+
},
47+
'.react-flow__attribution': {
48+
backgroundColor: theme.palette.background.paper,
49+
},
50+
}),
51+
labelBox: (theme) => ({
52+
flexGrow: 1,
53+
display: 'flex',
54+
alignItems: 'flex-end',
55+
whiteSpace: 'normal',
56+
wordBreak: 'break-word',
57+
fontWeight: 'bold',
58+
}),
59+
};
4960

5061
const NetworkModificationTree = ({ onNodeContextMenu, studyUuid, onTreePanelResize }) => {
5162
const dispatch = useDispatch();
@@ -65,8 +76,13 @@ const NetworkModificationTree = ({ onNodeContextMenu, studyUuid, onTreePanelResi
6576
const [nodes, setNodes, onNodesChange] = useNodesState([]);
6677
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
6778

79+
const [tooltipOpen, setTooltipOpen] = useState(false);
80+
const [tooltipContent, setTooltipContent] = useState();
81+
6882
const nodesMap = useMemo(() => new Map(nodes.map((n) => [n.id, n])), [nodes]);
6983

84+
const intl = useIntl();
85+
7086
const updateNodePositions = useCallback(() => {
7187
if (treeModel && treeModel.treeNodes?.length > 0) {
7288
const [treeNodeWithUpdatedPosition, securityGroupNodes] = getTreeNodesWithUpdatedPositions(
@@ -304,55 +320,102 @@ const NetworkModificationTree = ({ onNodeContextMenu, studyUuid, onTreePanelResi
304320
}
305321
}, [onTreePanelResize, handleFocusNode]);
306322

323+
const handleNodeMouseEnter = useCallback(
324+
(event, node) => {
325+
if (!node?.data || isRootNode(node)) {
326+
return;
327+
}
328+
329+
const content = (
330+
<Box style={{ whiteSpace: 'pre-line' }}>
331+
<Box sx={styles.labelBox}>{node.data.label}</Box>
332+
<Box>
333+
{intl.formatMessage({ id: 'nodeStatus' })} :{' '}
334+
{node.data.globalBuildStatus
335+
? intl.formatMessage({ id: node.data.globalBuildStatus })
336+
: intl.formatMessage({ id: 'NOT_BUILT' })}
337+
</Box>
338+
<Box>
339+
{intl.formatMessage({ id: 'nodeType' })} : {intl.formatMessage({ id: node.data.nodeType })}
340+
</Box>
341+
</Box>
342+
);
343+
344+
setTooltipContent(content);
345+
setTooltipOpen(true);
346+
},
347+
[intl]
348+
);
349+
350+
const handleNodeMouseLeave = useCallback(() => {
351+
setTooltipOpen(false);
352+
}, []);
353+
307354
return (
308-
<Box sx={styles}>
309-
<ReactFlow
310-
nodes={nodes}
311-
edges={edges}
312-
onNodesChange={handleNodesChange}
313-
onEdgesChange={onEdgesChange}
314-
fitView
315-
snapToGrid
316-
snapGrid={snapGrid}
317-
onNodeContextMenu={onNodeContextMenu}
318-
onNodeClick={onNodeClick}
319-
elementsSelectable
320-
selectNodesOnDrag={false}
321-
nodeTypes={nodeTypes}
322-
minZoom={0.1} // Lower value allows for more zoom out
323-
//maxZoom={2} // Higher value allows for more zoom in
324-
onNodeDragStop={handlePostNodeDragging}
325-
nodeClickDistance={5} // to avoid triggering onNodeDragStop instead of onNodeClick sometimes
326-
disableKeyboardA11y
327-
deleteKeyCode={null}
328-
defaultEdgeOptions={{
329-
type: 'smoothstep',
330-
pathOptions: {
331-
// TODO This negative offset and borderRadius values are needed to have round corners on the edge,
332-
// but because the nodes are not totally opaque, we can see the edges behind the nodes.
333-
// When the nodes are redesigned and hopefully the colors are set without transparency, we can use
334-
// the round edges by un-commenting the two lines below.
335-
//offset: -24,
336-
//borderRadius: 48,
355+
<Box sx={styles.modificationTree}>
356+
<Tooltip
357+
open={tooltipOpen}
358+
title={tooltipContent}
359+
componentsProps={{
360+
tooltip: {
361+
sx: {
362+
maxWidth: '720px',
363+
},
337364
},
338365
}}
366+
followCursor
367+
placement="right"
339368
>
340-
<Controls
341-
position="bottom-right"
342-
style={{ margin: '10px', marginBottom: '30px' }}
343-
showZoom={false}
344-
showInteractive={false}
345-
showFitView={false}
369+
<ReactFlow
370+
nodes={nodes}
371+
edges={edges}
372+
onNodesChange={handleNodesChange}
373+
onEdgesChange={onEdgesChange}
374+
fitView
375+
snapToGrid
376+
snapGrid={snapGrid}
377+
onNodeContextMenu={onNodeContextMenu}
378+
onNodeClick={onNodeClick}
379+
onNodeMouseEnter={handleNodeMouseEnter}
380+
onNodeMouseLeave={handleNodeMouseLeave}
381+
elementsSelectable
382+
selectNodesOnDrag={false}
383+
nodeTypes={nodeTypes}
384+
minZoom={0.1} // Lower value allows for more zoom out
385+
//maxZoom={2} // Higher value allows for more zoom in
386+
onNodeDragStop={handlePostNodeDragging}
387+
nodeClickDistance={5} // to avoid triggering onNodeDragStop instead of onNodeClick sometimes
388+
disableKeyboardA11y
389+
deleteKeyCode={null}
390+
defaultEdgeOptions={{
391+
type: 'smoothstep',
392+
pathOptions: {
393+
// TODO This negative offset and borderRadius values are needed to have round corners on the edge,
394+
// but because the nodes are not totally opaque, we can see the edges behind the nodes.
395+
// When the nodes are redesigned and hopefully the colors are set without transparency, we can use
396+
// the round edges by un-commenting the two lines below.
397+
//offset: -24,
398+
//borderRadius: 48,
399+
},
400+
}}
346401
>
347-
<TreeControlButton titleId="DisplayTheWholeTree" onClick={fitView}>
348-
<CropFreeIcon />
349-
</TreeControlButton>
350-
<TreeControlButton titleId="CenterSelectedNode" onClick={handleFocusNode}>
351-
<CenterFocusIcon />
352-
</TreeControlButton>
353-
</Controls>
354-
<RootNetworkPanel />
355-
</ReactFlow>
402+
<Controls
403+
position="bottom-right"
404+
style={{ margin: '10px', marginBottom: '30px' }}
405+
showZoom={false}
406+
showInteractive={false}
407+
showFitView={false}
408+
>
409+
<TreeControlButton titleId="DisplayTheWholeTree" onClick={fitView}>
410+
<CropFreeIcon />
411+
</TreeControlButton>
412+
<TreeControlButton titleId="CenterSelectedNode" onClick={handleFocusNode}>
413+
<CenterFocusIcon />
414+
</TreeControlButton>
415+
</Controls>
416+
<RootNetworkPanel />
417+
</ReactFlow>
418+
</Tooltip>
356419
</Box>
357420
);
358421
};

src/translations/en.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1615,5 +1615,9 @@
16151615
"Object": "Object",
16161616
"Array": "Array",
16171617
"Boolean": "Boolean",
1618-
"Enum": "Enumeration"
1618+
"Enum": "Enumeration",
1619+
"nodeType": "Type",
1620+
"nodeStatus": "Status",
1621+
"CONSTRUCTION": "Construction",
1622+
"SECURITY": "Security"
16191623
}

src/translations/fr.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1611,5 +1611,9 @@
16111611
"Object": "Objet",
16121612
"Array": "Tableau",
16131613
"Boolean": "Booléen",
1614-
"Enum": "Énumération"
1614+
"Enum": "Énumération",
1615+
"nodeType": "Type",
1616+
"nodeStatus": "Statut",
1617+
"CONSTRUCTION": "Construction",
1618+
"SECURITY": "Sécurité"
16151619
}

0 commit comments

Comments
 (0)