diff --git a/awx/ui/src/screens/TopologyView/Legend.js b/awx/ui/src/screens/TopologyView/Legend.js index 57a5b147ae..d674ecd4cf 100644 --- a/awx/ui/src/screens/TopologyView/Legend.js +++ b/awx/ui/src/screens/TopologyView/Legend.js @@ -1,3 +1,4 @@ +/* eslint i18next/no-literal-string: "off" */ import React from 'react'; import { t } from '@lingui/macro'; import styled from 'styled-components'; @@ -70,14 +71,14 @@ function Legend() { - + {t`Control node`} @@ -87,7 +88,7 @@ function Legend() { {t`Hybrid node`} @@ -95,7 +96,7 @@ function Legend() { {t`Hop node`} @@ -183,18 +184,18 @@ function Legend() { cx="10" cy="10" fill="transparent" - stroke-width="1px" + strokeWidth="1px" stroke="#ccc" - > + /> C @@ -210,19 +211,19 @@ function Legend() { cx="10" cy="10" fill="transparent" - stroke-dasharray="3" - stroke-width="1px" + strokeDasharray="5" + strokeWidth="1px" stroke="#ccc" - > + /> C diff --git a/awx/ui/src/screens/TopologyView/MeshGraph.js b/awx/ui/src/screens/TopologyView/MeshGraph.js index f520a75b69..9360580f70 100644 --- a/awx/ui/src/screens/TopologyView/MeshGraph.js +++ b/awx/ui/src/screens/TopologyView/MeshGraph.js @@ -41,6 +41,7 @@ const Loader = styled(ContentLoading)` background: white; `; function MeshGraph({ data, showLegend, zoom, setShowZoomControls }) { + const [storedNodes, setStoredNodes] = useState(null); const [isNodeSelected, setIsNodeSelected] = useState(false); const [selectedNode, setSelectedNode] = useState(null); const [simulationProgress, setSimulationProgress] = useState(null); @@ -75,6 +76,42 @@ function MeshGraph({ data, showLegend, zoom, setShowZoomControls }) { fetchDetails(); }, [selectedNode, fetchDetails]); + function updateNodeSVG(nodes) { + if (nodes) { + d3.selectAll('[class*="id-"]') + .data(nodes) + .attr('stroke-dasharray', (d) => (d.enabled ? `1 0` : `5`)); + } + } + + useEffect(() => { + function handleResize() { + d3.select('.simulation-loader').style('visibility', 'visible'); + setSelectedNode(null); + setIsNodeSelected(false); + draw(); + } + window.addEventListener('resize', debounce(handleResize, 500)); + handleResize(); + return () => window.removeEventListener('resize', handleResize); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + // update mesh when user toggles enabled/disabled slider + useEffect(() => { + if (instance?.id) { + const updatedNodes = storedNodes.map((n) => + n.id === instance.id ? { ...n, enabled: instance.enabled } : n + ); + setStoredNodes(updatedNodes); + } + }, [instance]); // eslint-disable-line react-hooks/exhaustive-deps + + useEffect(() => { + if (storedNodes) { + updateNodeSVG(storedNodes); + } + }, [storedNodes]); + const draw = () => { let width; let height; @@ -124,6 +161,7 @@ function MeshGraph({ data, showLegend, zoom, setShowZoomControls }) { } function ended({ nodes, links }) { + setStoredNodes(nodes); // Remove loading screen d3.select('.simulation-loader').style('visibility', 'hidden'); setShowZoomControls(true); @@ -205,7 +243,7 @@ function MeshGraph({ data, showLegend, zoom, setShowZoomControls }) { .attr('class', (d) => d.node_type) .attr('class', (d) => `id-${d.id}`) .attr('fill', DEFAULT_NODE_COLOR) - .attr('stroke-dasharray', (d) => (d.enabled ? null : 3)) + .attr('stroke-dasharray', (d) => (d.enabled ? `1 0` : `5`)) .attr('stroke', DEFAULT_NODE_STROKE_COLOR); // node type labels @@ -341,18 +379,6 @@ function MeshGraph({ data, showLegend, zoom, setShowZoomControls }) { } }; - useEffect(() => { - function handleResize() { - d3.select('.simulation-loader').style('visibility', 'visible'); - setSelectedNode(null); - setIsNodeSelected(false); - draw(); - } - window.addEventListener('resize', debounce(handleResize, 500)); - handleResize(); - return () => window.removeEventListener('resize', handleResize); - }, []); // eslint-disable-line react-hooks/exhaustive-deps - return (
{showLegend && }