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={
+
+ }
+ 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={
-
- }
- 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"