diff --git a/e2e/suite1/specs/exemplars.spec.ts b/e2e/suite1/specs/exemplars.spec.ts index c93b2da6b79..d11687ed800 100644 --- a/e2e/suite1/specs/exemplars.spec.ts +++ b/e2e/suite1/specs/exemplars.spec.ts @@ -65,8 +65,6 @@ describe('Exemplars', () => { e2e.components.DataSource.Prometheus.exemplarMarker().first().trigger('mouseover'); e2e().contains('Query with gdev-tempo').click(); - - e2e().get('[aria-label="Node: app"]').should('be.visible'); e2e.components.TraceViewer.spanBar().should('have.length', 11); }); }); diff --git a/e2e/suite1/specs/trace-view-scrolling.spec.ts b/e2e/suite1/specs/trace-view-scrolling.spec.ts index ecabfd5ed8b..f6daacea4a2 100644 --- a/e2e/suite1/specs/trace-view-scrolling.spec.ts +++ b/e2e/suite1/specs/trace-view-scrolling.spec.ts @@ -29,7 +29,7 @@ describe('Trace view', () => { e2e.pages.Explore.General.scrollBar().scrollTo('center'); - // After scrolling we should have 139 spans instead of the first 100 - e2e.components.TraceViewer.spanBar().should('have.length', 139); + // After scrolling we should load more spans + e2e.components.TraceViewer.spanBar().should('have.length', 140); }); }); diff --git a/packages/grafana-ui/src/components/Collapse/Collapse.tsx b/packages/grafana-ui/src/components/Collapse/Collapse.tsx index aa4554984c7..908457718a5 100644 --- a/packages/grafana-ui/src/components/Collapse/Collapse.tsx +++ b/packages/grafana-ui/src/components/Collapse/Collapse.tsx @@ -76,6 +76,10 @@ const getStyles = (theme: GrafanaTheme2) => ({ margin-right: ${theme.spacing(1)}; font-size: ${theme.typography.size.md}; `, + icon: css` + label: collapse__icon; + margin-left: -5px; + `, }); export interface Props { @@ -133,7 +137,7 @@ export const Collapse: FunctionComponent = ({ return (
- {collapsible && } + {collapsible && }
{label}
{isOpen && ( diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index 7c49109f840..0ea2a18abc2 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -271,7 +271,7 @@ export class Explore extends React.PureComponent { ); } diff --git a/public/app/features/explore/NodeGraphContainer.test.tsx b/public/app/features/explore/NodeGraphContainer.test.tsx new file mode 100644 index 00000000000..01d5960af01 --- /dev/null +++ b/public/app/features/explore/NodeGraphContainer.test.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { UnconnectedNodeGraphContainer } from './NodeGraphContainer'; +import { getDefaultTimeRange, MutableDataFrame } from '@grafana/data'; +import { ExploreId } from '../../types'; + +describe('NodeGraphContainer', () => { + it('is collapsed if shown with traces', () => { + const { container } = render( + {}) as any} + withTraceView={true} + /> + ); + + // Make sure we only show header in the collapsible + expect(container.firstChild?.childNodes.length).toBe(1); + }); + + it('shows the graph if not with trace view', () => { + const { container } = render( + {}) as any} + /> + ); + + expect(container.firstChild?.childNodes.length).toBe(2); + expect(container.querySelector('svg')).toBeInTheDocument(); + }); +}); + +const emptyFrame = new MutableDataFrame(); + +export const nodes = new MutableDataFrame({ + fields: toFields([ + ['id', ['3fa414edcef6ad90']], + ['title', ['tempo-querier']], + ['subTitle', ['HTTP GET - api_traces_traceid']], + ['mainStat', ['1049.14ms (100%)']], + ['secondaryStat', ['1047.29ms (99.82%)']], + ['color', [0.9982395121342127]], + ]), +}); + +export function toFields(fields: Array<[string, any[]]>) { + return fields.map(([name, values]) => { + return { name, values }; + }); +} diff --git a/public/app/features/explore/NodeGraphContainer.tsx b/public/app/features/explore/NodeGraphContainer.tsx index a15d27e8213..24f576c976b 100644 --- a/public/app/features/explore/NodeGraphContainer.tsx +++ b/public/app/features/explore/NodeGraphContainer.tsx @@ -1,11 +1,22 @@ -import React, { useState } from 'react'; -import { Badge, Collapse } from '@grafana/ui'; -import { DataFrame, TimeRange } from '@grafana/data'; +import React from 'react'; +import { useToggle } from 'react-use'; +import { Badge, Collapse, useStyles2 } from '@grafana/ui'; +import { DataFrame, GrafanaTheme2, TimeRange } from '@grafana/data'; +import { css } from '@emotion/css'; import { ExploreId, StoreState } from '../../types'; import { splitOpen } from './state/main'; import { connect, ConnectedProps } from 'react-redux'; import { useLinks } from './utils/links'; import { NodeGraph } from '../../plugins/panel/nodeGraph'; +import { useCategorizeFrames } from '../../plugins/panel/nodeGraph/useCategorizeFrames'; + +const getStyles = (theme: GrafanaTheme2) => ({ + warningText: css` + label: warningText; + font-size: ${theme.typography.bodySmall.fontSize}; + color: ${theme.colors.text.secondary}; + `, +}); interface Props { // Edges and Nodes are separate frames @@ -13,26 +24,35 @@ interface Props { exploreId: ExploreId; range: TimeRange; splitOpen: typeof splitOpen; - short?: boolean; + // When showing the node graph together with trace view we do some changes so it works better. + withTraceView?: boolean; } export function UnconnectedNodeGraphContainer(props: Props & ConnectedProps) { - const { dataFrames, range, splitOpen, short } = props; + const { dataFrames, range, splitOpen, withTraceView } = props; const getLinks = useLinks(range, splitOpen); + const styles = useStyles2(getStyles); - const [open, setOpen] = useState(true); + const { nodes } = useCategorizeFrames(dataFrames); + const [open, toggleOpen] = useToggle(false); + const countWarning = + withTraceView && nodes[0]?.length > 1000 ? ( + ({nodes[0].length} nodes, can be slow to load) + ) : null; return ( - Node graph + Node graph{countWarning}{' '} + } - collapsible={short} - isOpen={short ? open : true} - onToggle={short ? () => setOpen(!open) : undefined} + collapsible={withTraceView} + // We allow collapsing this only when it is shown together with trace view. + isOpen={withTraceView ? open : true} + onToggle={withTraceView ? () => toggleOpen() : undefined} > -
+