From 71400f9b6a6604d055e239b42023e4003807d6fd Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Fri, 13 Mar 2020 10:15:43 -0700 Subject: [PATCH] [APM] Service maps fix for service name/env filtering & centering (#59726) * - adds primary color highlighting to connected edges of the focused service - fixes z-indexing issues with overlapping edges when hovering on a service node - always centers on the focused service node after layout reflows * re-centers the already focused service node when the focus button is clicked * remove environemnt filter when querying for sample trace ids * - fixes missing query params in the generated hrefs for details and focused service navigation - fix layout bug by passing undefined for roots (instead of []) in service maps layout when no roots are found * Revert "remove environemnt filter when querying for sample trace ids" This reverts commit d482aa124b1f2c47da01d4386c2b88108bc94275. * Fixes extra prop from a merge conflict --- .../components/app/ServiceMap/Cytoscape.tsx | 14 ++++++++-- .../app/ServiceMap/Popover/Buttons.tsx | 27 +++++-------------- .../app/ServiceMap/Popover/Contents.tsx | 3 --- .../app/ServiceMap/Popover/index.tsx | 11 ++++++-- .../app/ServiceMap/cytoscapeOptions.ts | 25 ++++++++++++++--- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx index d636f8b1f4d52..ae6b06b10fd1d 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx @@ -63,7 +63,7 @@ function getLayoutOptions( ): cytoscape.LayoutOptions { return { name: 'breadthfirst', - roots: selectedRoots, + roots: selectedRoots.length ? selectedRoots : undefined, fit: true, padding: nodeHeight, spacingFactor: 0.85, @@ -111,18 +111,28 @@ export function Cytoscape({ const dataHandler = useCallback( event => { if (cy) { + cy.edges().removeClass('highlight'); + + if (serviceName) { + const focusedNode = cy.getElementById(serviceName); + focusedNode.connectedEdges().addClass('highlight'); + } + // Add the "primary" class to the node if its id matches the serviceName. if (cy.nodes().length > 0 && serviceName) { cy.nodes().removeClass('primary'); cy.getElementById(serviceName).addClass('primary'); } - if (event.cy.elements().length > 0) { const selectedRoots = selectRoots(event.cy); const layout = cy.layout( getLayoutOptions(selectedRoots, height, width) ); layout.one('layoutstop', () => { + if (serviceName) { + const focusedNode = cy.getElementById(serviceName); + cy.center(focusedNode); + } // show elements after layout is applied cy.elements().removeClass('invisible'); }); diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx index a8c45c83a382a..8041554756adc 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx @@ -11,30 +11,29 @@ import { i18n } from '@kbn/i18n'; import React, { MouseEvent } from 'react'; import { useUrlParams } from '../../../../hooks/useUrlParams'; import { getAPMHref } from '../../../shared/Links/apm/APMLink'; +import { APMQueryParams } from '../../../shared/Links/url_helpers'; interface ButtonsProps { - focusedServiceName?: string; onFocusClick?: (event: MouseEvent) => void; selectedNodeServiceName: string; } export function Buttons({ - focusedServiceName, onFocusClick = () => {}, selectedNodeServiceName }: ButtonsProps) { - const currentSearch = useUrlParams().urlParams.kuery ?? ''; + const urlParams = useUrlParams().urlParams as APMQueryParams; const detailsUrl = getAPMHref( `/services/${selectedNodeServiceName}/transactions`, - currentSearch + '', + urlParams ); const focusUrl = getAPMHref( `/services/${selectedNodeServiceName}/service-map`, - currentSearch + '', + urlParams ); - const isAlreadyFocused = focusedServiceName === selectedNodeServiceName; - return ( <> @@ -45,19 +44,7 @@ export function Buttons({ - + {i18n.translate('xpack.apm.serviceMap.focusMapButtonText', { defaultMessage: 'Focus map' })} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx index 405bd855898b7..7db064632a7f1 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx @@ -19,7 +19,6 @@ import { ServiceMetricFetcher } from './ServiceMetricFetcher'; const popoverMinWidth = 280; interface ContentsProps { - focusedServiceName?: string; isService: boolean; label: string; onFocusClick: () => void; @@ -29,7 +28,6 @@ interface ContentsProps { export function Contents({ selectedNodeData, - focusedServiceName, isService, label, onFocusClick, @@ -60,7 +58,6 @@ export function Contents({ {isService && ( diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx index 377496f370667..f9dc4d4b14aab 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx @@ -85,6 +85,14 @@ export function Popover({ focusedServiceName }: PopoverProps) { } }, [popoverRef, x, y]); + const centerSelectedNode = useCallback(() => { + if (cy) { + cy.center(cy.getElementById(selectedNodeServiceName)); + } + }, [cy, selectedNodeServiceName]); + + const isAlreadyFocused = focusedServiceName === selectedNodeServiceName; + return ( ); diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts index 1a2feb5a097e5..87008d8790788 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts @@ -13,6 +13,10 @@ export const animationOptions: cytoscape.AnimationOptions = { easing: theme.euiAnimSlightBounce }; const lineColor = '#C5CCD7'; +const zIndexNode = 200; +const zIndexEdge = 100; +const zIndexEdgeHighlight = 110; +const zIndexEdgeHover = 120; export const nodeHeight = parseInt(theme.avatarSizing.l.size, 10); function isService(el: cytoscape.NodeSingular) { @@ -62,7 +66,8 @@ const style: cytoscape.Stylesheet[] = [ 'text-max-width': '200px', 'text-valign': 'bottom', 'text-wrap': 'ellipsis', - width: theme.avatarSizing.l.size + width: theme.avatarSizing.l.size, + 'z-index': zIndexNode } }, { @@ -81,7 +86,8 @@ const style: cytoscape.Stylesheet[] = [ // @ts-ignore 'target-distance-from-node': theme.paddingSizes.xs, width: 1, - 'source-arrow-shape': 'none' + 'source-arrow-shape': 'none', + 'z-index': zIndexEdge } }, { @@ -103,7 +109,9 @@ const style: cytoscape.Stylesheet[] = [ { selector: 'edge.nodeHover', style: { - width: 4 + width: 4, + // @ts-ignore + 'z-index': zIndexEdgeHover } }, { @@ -111,6 +119,17 @@ const style: cytoscape.Stylesheet[] = [ style: { 'border-width': 4 } + }, + { + selector: 'edge.highlight', + style: { + width: 2, + 'line-color': theme.euiColorPrimary, + 'source-arrow-color': theme.euiColorPrimary, + 'target-arrow-color': theme.euiColorPrimary, + // @ts-ignore + 'z-index': zIndexEdgeHighlight + } } ];