diff --git a/packages/kbn-storybook/storybook_config/config.js b/packages/kbn-storybook/storybook_config/config.js index a7975773e675..d97bd3f7c2dc 100644 --- a/packages/kbn-storybook/storybook_config/config.js +++ b/packages/kbn-storybook/storybook_config/config.js @@ -55,7 +55,7 @@ addParameters({ brandTitle: 'Kibana Storybook', brandUrl: 'https://github.com/elastic/kibana/tree/master/packages/kbn-storybook', }), - showPanel: true, + showPanel: false, isFullscreen: false, panelPosition: 'bottom', isToolshown: true, diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx index 52941391ca36..6f7b743d8b77 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx @@ -9,8 +9,12 @@ import { storiesOf } from '@storybook/react'; import cytoscape from 'cytoscape'; import React from 'react'; import { Cytoscape } from './Cytoscape'; +import { getCytoscapeElements } from './get_cytoscape_elements'; +import serviceMapResponse from './cytoscape-layout-test-response.json'; import { iconForNode } from './icons'; +const elementsFromResponses = getCytoscapeElements([serviceMapResponse], ''); + storiesOf('app/ServiceMap/Cytoscape', module).add( 'example', () => { @@ -49,11 +53,13 @@ storiesOf('app/ServiceMap/Cytoscape', module).add( } ]; const height = 300; + const width = 1340; const serviceName = 'opbeans-python'; return ( ); @@ -66,114 +72,137 @@ storiesOf('app/ServiceMap/Cytoscape', module).add( } ); -storiesOf('app/ServiceMap/Cytoscape', module).add( - 'node icons', - () => { - const cy = cytoscape(); - const elements = [ - { data: { id: 'default', label: 'default', type: undefined } }, - { data: { id: 'cache', label: 'cache', type: 'cache' } }, - { data: { id: 'database', label: 'database', type: 'database' } }, - { data: { id: 'external', label: 'external', type: 'external' } }, - { data: { id: 'messaging', label: 'messaging', type: 'messaging' } }, +storiesOf('app/ServiceMap/Cytoscape', module) + .add( + 'node icons', + () => { + const cy = cytoscape(); + const elements = [ + { data: { id: 'default', label: 'default', type: undefined } }, + { data: { id: 'cache', label: 'cache', type: 'cache' } }, + { data: { id: 'database', label: 'database', type: 'database' } }, + { data: { id: 'external', label: 'external', type: 'external' } }, + { data: { id: 'messaging', label: 'messaging', type: 'messaging' } }, - { - data: { - id: 'dotnet', - label: 'dotnet service', - type: 'service', - agentName: 'dotnet' - } - }, - { - data: { - id: 'go', - label: 'go service', - type: 'service', - agentName: 'go' - } - }, - { - data: { - id: 'java', - label: 'java service', - type: 'service', - agentName: 'java' - } - }, - { - data: { - id: 'js-base', - label: 'js-base service', - type: 'service', - agentName: 'js-base' - } - }, - { - data: { - id: 'nodejs', - label: 'nodejs service', - type: 'service', - agentName: 'nodejs' - } - }, - { - data: { - id: 'php', - label: 'php service', - type: 'service', - agentName: 'php' - } - }, - { - data: { - id: 'python', - label: 'python service', - type: 'service', - agentName: 'python' - } - }, - { - data: { - id: 'ruby', - label: 'ruby service', - type: 'service', - agentName: 'ruby' + { + data: { + id: 'dotnet', + label: 'dotnet service', + type: 'service', + agentName: 'dotnet' + } + }, + { + data: { + id: 'go', + label: 'go service', + type: 'service', + agentName: 'go' + } + }, + { + data: { + id: 'java', + label: 'java service', + type: 'service', + agentName: 'java' + } + }, + { + data: { + id: 'js-base', + label: 'js-base service', + type: 'service', + agentName: 'js-base' + } + }, + { + data: { + id: 'nodejs', + label: 'nodejs service', + type: 'service', + agentName: 'nodejs' + } + }, + { + data: { + id: 'php', + label: 'php service', + type: 'service', + agentName: 'php' + } + }, + { + data: { + id: 'python', + label: 'python service', + type: 'service', + agentName: 'python' + } + }, + { + data: { + id: 'ruby', + label: 'ruby service', + type: 'service', + agentName: 'ruby' + } } + ]; + cy.add(elements); + + return ( + + {cy.nodes().map(node => ( + + + agentName: {node.data('agentName') || 'undefined'}, type:{' '} + {node.data('type') || 'undefined'} + + } + icon={ + {node.data('label')} + } + title={node.data('label')} + /> + + ))} + + ); + }, + { + info: { + propTables: false, + source: false } - ]; - cy.add(elements); - - return ( - - {cy.nodes().map(node => ( - - - agentName: {node.data('agentName') || 'undefined'}, type:{' '} - {node.data('type') || 'undefined'} - - } - icon={ - {node.data('label')} - } - title={node.data('label')} - /> - - ))} - - ); - }, - { - info: { - propTables: false, - source: false } - } -); + ) + .add( + 'layout', + () => { + const height = 640; + const width = 1340; + const serviceName = undefined; // global service map + return ( + + ); + }, + { + info: { + source: false + } + } + ) + .addParameters({ options: { showPanel: false } }); 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 f7d74bee1aa5..64b82fc8886c 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 @@ -10,13 +10,16 @@ import React, { useRef, useEffect, ReactNode, - createContext + createContext, + useCallback } from 'react'; import cytoscape from 'cytoscape'; -import dagre from 'cytoscape-dagre'; -import { cytoscapeOptions } from './cytoscapeOptions'; - -cytoscape.use(dagre); +import { isRumAgentName } from '../../../../../../../plugins/apm/common/agent_name'; +import { + cytoscapeOptions, + nodeHeight, + animationOptions +} from './cytoscapeOptions'; export const CytoscapeContext = createContext( undefined @@ -26,6 +29,7 @@ interface CytoscapeProps { children?: ReactNode; elements: cytoscape.ElementDefinition[]; height: number; + width: number; serviceName?: string; style?: CSSProperties; } @@ -52,19 +56,83 @@ function useCytoscape(options: cytoscape.CytoscapeOptions) { return [ref, cy] as [React.MutableRefObject, cytoscape.Core | undefined]; } +function getLayoutOptions( + selectedRoots: string[], + height: number, + width: number +): cytoscape.LayoutOptions { + return { + name: 'breadthfirst', + roots: selectedRoots, + fit: true, + padding: nodeHeight, + spacingFactor: 0.85, + animate: true, + animationEasing: animationOptions.easing, + animationDuration: animationOptions.duration, + // Rotate nodes from top -> bottom to display left -> right + // @ts-ignore + transform: (node: any, { x, y }: cytoscape.Position) => ({ x: y, y: -x }), + // swap width/height of boundingBox to compensation for the rotation + boundingBox: { x1: 0, y1: 0, w: height, h: width } + }; +} + +function selectRoots(elements: cytoscape.ElementDefinition[]): string[] { + const nodes = cytoscape({ elements }).nodes(); + const unconnectedNodes = nodes.roots().intersection(nodes.leaves()); + const rumNodes = nodes.filter(node => isRumAgentName(node.data('agentName'))); + return rumNodes.union(unconnectedNodes).map(node => node.id()); +} + export function Cytoscape({ children, elements, height, + width, serviceName, style }: CytoscapeProps) { - const [ref, cy] = useCytoscape({ ...cytoscapeOptions, elements }); + const initialElements = elements.map(element => ({ + ...element, + // prevents flash of unstyled elements + classes: [element.classes, 'invisible'].join(' ').trim() + })); + + const [ref, cy] = useCytoscape({ + ...cytoscapeOptions, + elements: initialElements + }); // Add the height to the div style. The height is a separate prop because it // is required and can trigger rendering when changed. const divStyle = { ...style, height }; + const dataHandler = useCallback( + event => { + if (cy) { + // 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(elements); + const layout = cy.layout( + getLayoutOptions(selectedRoots, height, width) + ); + layout.one('layoutstop', () => { + // show elements after layout is applied + cy.elements().removeClass('invisible'); + }); + layout.run(); + } + } + }, + [cy, serviceName, elements, height, width] + ); + // Trigger a custom "data" event when data changes useEffect(() => { if (cy) { @@ -75,19 +143,6 @@ export function Cytoscape({ // Set up cytoscape event handlers useEffect(() => { - const dataHandler: cytoscape.EventHandler = event => { - if (cy) { - // 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) { - cy.layout(cytoscapeOptions.layout as cytoscape.LayoutOptions).run(); - } - } - }; const mouseoverHandler: cytoscape.EventHandler = event => { event.target.addClass('hover'); event.target.connectedEdges().addClass('nodeHover'); @@ -99,18 +154,23 @@ export function Cytoscape({ if (cy) { cy.on('data', dataHandler); + cy.ready(dataHandler); cy.on('mouseover', 'edge, node', mouseoverHandler); cy.on('mouseout', 'edge, node', mouseoutHandler); } return () => { if (cy) { - cy.removeListener('data', undefined, dataHandler); + cy.removeListener( + 'data', + undefined, + dataHandler as cytoscape.EventHandler + ); cy.removeListener('mouseover', 'edge, node', mouseoverHandler); cy.removeListener('mouseout', 'edge, node', mouseoutHandler); } }; - }, [cy, serviceName]); + }, [cy, dataHandler, serviceName]); return ( diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json new file mode 100644 index 000000000000..924330b1f1d8 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json @@ -0,0 +1 @@ +{"connections":[{"source":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"},"destination":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"}},{"source":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"},"destination":{"destination.address":"opbeans-java","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"destination.address":"opbeans-java","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"},"destination":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"destination.address":"opbeans-dotnet","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"},"destination":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"destination.address":"opbeans-ruby","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":null,"service.name":"client","agent.name":"js-base"},"destination":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"destination.address":"opbeans-node","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"},"destination":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"destination.address":"postgres","span.subtype":"postgresql","span.type":"db"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"}},{"source":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"},"destination":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"}},{"source":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"},"destination":{"destination.address":"opbeans-ruby","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"destination.address":"opbeans-go","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"},"destination":{"destination.address":"opbeans-node","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"}},{"source":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"},"destination":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"destination.address":"opbeans-java","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"},"destination":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"destination.address":"opbeans-go","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"},"destination":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"}},{"source":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"},"destination":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"destination.address":"opbeans-ruby","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"destination.address":"postgres","span.subtype":"postgresql","span.type":"db"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"destination.address":"opbeans-node","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"},"destination":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"}},{"source":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"},"destination":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"}},{"source":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"},"destination":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"destination.address":"opbeans-python","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"},"destination":{"destination.address":"opbeans-python","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"}},{"source":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"},"destination":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"}},{"source":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"},"destination":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"}},{"source":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"},"destination":{"destination.address":"opbeans-go","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"},"destination":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"destination.address":"opbeans-dotnet","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"},"destination":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"}},{"source":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"},"destination":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"}},{"source":{"service.environment":null,"service.name":"client","agent.name":"js-base"},"destination":{"destination.address":"opbeans-node","span.subtype":null,"span.type":"resource"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"destination.address":"opbeans-python","span.subtype":"http","span.type":"external"}},{"source":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"},"destination":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"}},{"source":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"},"destination":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"}},{"source":{"service.environment":null,"service.name":"client","agent.name":"js-base"},"destination":{"destination.address":"opbeans-node","span.subtype":null,"span.type":"external"}}],"discoveredServices":[{"from":{"destination.address":"opbeans-dotnet","span.subtype":"http","span.type":"external"},"to":{"service.environment":"production","service.name":"opbeans-dotnet","agent.name":"dotnet"}},{"from":{"destination.address":"opbeans-node","span.subtype":null,"span.type":"external"},"to":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"}},{"from":{"destination.address":"opbeans-java","span.subtype":"http","span.type":"external"},"to":{"service.environment":"production","service.name":"opbeans-java","agent.name":"java"}},{"from":{"destination.address":"opbeans-go","span.subtype":"http","span.type":"external"},"to":{"service.environment":"production","service.name":"opbeans-go","agent.name":"go"}},{"from":{"destination.address":"opbeans-python","span.subtype":"http","span.type":"external"},"to":{"service.environment":"production","service.name":"opbeans-python","agent.name":"python"}},{"from":{"destination.address":"opbeans-node","span.subtype":"http","span.type":"external"},"to":{"service.environment":"production","service.name":"opbeans-node","agent.name":"nodejs"}},{"from":{"destination.address":"opbeans-ruby","span.subtype":"http","span.type":"external"},"to":{"service.environment":"production","service.name":"opbeans-ruby","agent.name":"ruby"}}],"services":[{"service.name":"apm-server","agent.name":"go","service.environment":null},{"service.name":"opbeans-python","agent.name":"python","service.environment":null},{"service.name":"opbeans-ruby","agent.name":"ruby","service.environment":null},{"service.name":"opbeans-node","agent.name":"nodejs","service.environment":null},{"service.name":"opbeans-go","agent.name":"go","service.environment":null},{"service.name":"opbeans-java","agent.name":"java","service.environment":null},{"service.name":"opbeans-dotnet","agent.name":"dotnet","service.environment":null},{"service.name":"client","agent.name":"js-base","service.environment":null}]} 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 8411169dbc94..1a2feb5a097e 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 @@ -15,17 +15,6 @@ export const animationOptions: cytoscape.AnimationOptions = { const lineColor = '#C5CCD7'; export const nodeHeight = parseInt(theme.avatarSizing.l.size, 10); -const layout = { - name: 'dagre', - nodeDimensionsIncludeLabels: true, - rankDir: 'LR', - animate: true, - animationEasing: animationOptions.easing, - animationDuration: animationOptions.duration, - fit: true, - padding: nodeHeight -}; - function isService(el: cytoscape.NodeSingular) { return el.data('type') === 'service'; } @@ -79,7 +68,9 @@ const style: cytoscape.Stylesheet[] = [ { selector: 'edge', style: { - 'curve-style': 'bezier', + 'curve-style': 'taxi', + // @ts-ignore + 'taxi-direction': 'rightward', 'line-color': lineColor, 'overlay-opacity': 0, 'target-arrow-color': lineColor, @@ -103,13 +94,29 @@ const style: cytoscape.Stylesheet[] = [ 'source-distance-from-node': theme.paddingSizes.xs, 'target-distance-from-node': theme.paddingSizes.xs } + }, + // @ts-ignore + { + selector: '.invisible', + style: { visibility: 'hidden' } + }, + { + selector: 'edge.nodeHover', + style: { + width: 4 + } + }, + { + selector: 'node.hover', + style: { + 'border-width': 4 + } } ]; export const cytoscapeOptions: cytoscape.CytoscapeOptions = { autoungrabify: true, boxSelectionEnabled: false, - layout, maxZoom: 3, minZoom: 0.2, style diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx index b14ecaa803f6..d5f0728a7ff1 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -32,7 +32,7 @@ import { Cytoscape } from './Cytoscape'; import { getCytoscapeElements } from './get_cytoscape_elements'; import { PlatinumLicensePrompt } from './PlatinumLicensePrompt'; import { Popover } from './Popover'; -import { useRefHeight } from './useRefHeight'; +import { useRefDimensions } from './useRefDimensions'; interface ServiceMapProps { serviceName?: string; @@ -196,7 +196,7 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [elements]); - const [wrapperRef, height] = useRefHeight(); + const { ref: wrapperRef, width, height } = useRefDimensions(); if (!license) { return null; @@ -211,6 +211,7 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { elements={renderedElements.current} serviceName={serviceName} height={height} + width={width} style={cytoscapeDivStyle} > diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/useRefHeight.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/useRefDimensions.ts similarity index 56% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/useRefHeight.ts rename to x-pack/legacy/plugins/apm/public/components/app/ServiceMap/useRefDimensions.ts index b8fba47acd2d..c8639b334f66 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/useRefHeight.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/useRefDimensions.ts @@ -3,18 +3,19 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { MutableRefObject, useRef } from 'react'; +import { useRef } from 'react'; import { useWindowSize } from 'react-use'; -export function useRefHeight(): [ - MutableRefObject, - number -] { +export function useRefDimensions() { const ref = useRef(null); const windowHeight = useWindowSize().height; - const topOffset = ref.current?.getBoundingClientRect()?.top ?? 0; - const height = ref.current ? windowHeight - topOffset : 0; + if (!ref.current) { + return { ref, width: 0, height: 0 }; + } - return [ref, height]; + const { top, width } = ref.current.getBoundingClientRect(); + const height = windowHeight - top; + + return { ref, width, height }; } diff --git a/x-pack/package.json b/x-pack/package.json index 96e06dd71b3f..585d05b3c8a1 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -222,7 +222,6 @@ "copy-to-clipboard": "^3.0.8", "cronstrue": "^1.51.0", "cytoscape": "^3.10.0", - "cytoscape-dagre": "^2.2.2", "d3": "3.5.17", "d3-scale": "1.0.7", "dedent": "^0.7.0", diff --git a/x-pack/plugins/apm/typings/cytoscape_dagre.d.ts b/x-pack/plugins/apm/typings/cytoscape_dagre.d.ts deleted file mode 100644 index b5bbdfc14d9d..000000000000 --- a/x-pack/plugins/apm/typings/cytoscape_dagre.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -declare module 'cytoscape-dagre'; diff --git a/yarn.lock b/yarn.lock index e4d5dcce5bca..dde08490d62f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10695,13 +10695,6 @@ cypress@^4.0.2: url "0.11.0" yauzl "2.10.0" -cytoscape-dagre@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/cytoscape-dagre/-/cytoscape-dagre-2.2.2.tgz#5f32a85c0ba835f167efee531df9e89ac58ff411" - integrity sha512-zsg36qNwua/L2stJSWkcbSDcvW3E6VZf6KRe6aLnQJxuXuz89tMqI5EVYVKEcNBgzTEzFMFv0PE3T0nD4m6VDw== - dependencies: - dagre "^0.8.2" - cytoscape@^3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.10.0.tgz#3b462e0d35121ecd2d2702f470915fd6dae01777" @@ -10967,14 +10960,6 @@ d@1: dependencies: es5-ext "^0.10.9" -dagre@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.4.tgz#26b9fb8f7bdc60c6110a0458c375261836786061" - integrity sha512-Dj0csFDrWYKdavwROb9FccHfTC4fJbyF/oJdL9LNZJ8WUvl968P6PAKEriGqfbdArVJEmmfA+UyumgWEwcHU6A== - dependencies: - graphlib "^2.1.7" - lodash "^4.17.4" - damerau-levenshtein@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514" @@ -15304,13 +15289,6 @@ graceful-fs@~1.1: resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= -graphlib@^2.1.7: - version "2.1.7" - resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.7.tgz#b6a69f9f44bd9de3963ce6804a2fc9e73d86aecc" - integrity sha512-TyI9jIy2J4j0qgPmOOrHTCtpPqJGN/aurBwc6ZT+bRii+di1I+Wv3obRhVrmBEXet+qkMaEX67dXrwsd3QQM6w== - dependencies: - lodash "^4.17.5" - graphql-anywhere@^4.1.0-alpha.0: version "4.1.16" resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-4.1.16.tgz#82bb59643e30183cfb7b485ed4262a7b39d8a6c1"