mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Trace View: Span list visual update (#75238)
* Show color of row as a border under the row. Hide service name for sequential spans * Increase default span name column width. Smaller font for service and span names in span list * New background color on spans. Fixed hover of indent markers * Service name and span name style tweaks * Collapse hidden levels * Fixed test * Small tweak to Buffer size to make sure tests pass * Trigger runs * Update betterer results * Address comment * Style tweaks * Remove duplicated code * Rollback change to join <span> since they are needed for the tests
This commit is contained in:
parent
65fa94b16b
commit
607a664ef4
@ -4163,14 +4163,17 @@ exports[`better eslint`] = {
|
|||||||
],
|
],
|
||||||
"public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanLinks.tsx:5381": [
|
"public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanLinks.tsx:5381": [
|
||||||
[0, 0, 0, "Styles should be written using objects.", "0"],
|
[0, 0, 0, "Styles should be written using objects.", "0"],
|
||||||
[0, 0, 0, "Styles should be written using objects.", "1"]
|
[0, 0, 0, "Styles should be written using objects.", "1"],
|
||||||
|
[0, 0, 0, "Styles should be written using objects.", "2"],
|
||||||
|
[0, 0, 0, "Styles should be written using objects.", "3"]
|
||||||
],
|
],
|
||||||
"public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanTreeOffset.tsx:5381": [
|
"public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanTreeOffset.tsx:5381": [
|
||||||
[0, 0, 0, "Styles should be written using objects.", "0"],
|
[0, 0, 0, "Styles should be written using objects.", "0"],
|
||||||
[0, 0, 0, "Styles should be written using objects.", "1"],
|
[0, 0, 0, "Styles should be written using objects.", "1"],
|
||||||
[0, 0, 0, "Styles should be written using objects.", "2"],
|
[0, 0, 0, "Styles should be written using objects.", "2"],
|
||||||
[0, 0, 0, "Styles should be written using objects.", "3"],
|
[0, 0, 0, "Styles should be written using objects.", "3"],
|
||||||
[0, 0, 0, "Styles should be written using objects.", "4"]
|
[0, 0, 0, "Styles should be written using objects.", "4"],
|
||||||
|
[0, 0, 0, "Styles should be written using objects.", "5"]
|
||||||
],
|
],
|
||||||
"public/app/features/explore/TraceView/components/TraceTimelineViewer/Ticks.tsx:5381": [
|
"public/app/features/explore/TraceView/components/TraceTimelineViewer/Ticks.tsx:5381": [
|
||||||
[0, 0, 0, "Styles should be written using objects.", "0"],
|
[0, 0, 0, "Styles should be written using objects.", "0"],
|
||||||
|
@ -91,7 +91,7 @@ export function TraceView(props: Props) {
|
|||||||
/**
|
/**
|
||||||
* Keeps state of resizable name column width
|
* Keeps state of resizable name column width
|
||||||
*/
|
*/
|
||||||
const [spanNameColumnWidth, setSpanNameColumnWidth] = useState(0.25);
|
const [spanNameColumnWidth, setSpanNameColumnWidth] = useState(0.4);
|
||||||
|
|
||||||
const [focusedSpanId, createFocusSpanLink] = useFocusSpanLink({
|
const [focusedSpanId, createFocusSpanLink] = useFocusSpanLink({
|
||||||
refId: props.dataFrames[0]?.refId,
|
refId: props.dataFrames[0]?.refId,
|
||||||
|
@ -73,7 +73,8 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, showSpanFilterMatchesOnly
|
|||||||
`,
|
`,
|
||||||
endpointName: css`
|
endpointName: css`
|
||||||
label: endpointName;
|
label: endpointName;
|
||||||
color: ${autoColor(theme, '#808080')};
|
color: ${autoColor(theme, '#484848')};
|
||||||
|
font-size: 0.9em;
|
||||||
`,
|
`,
|
||||||
view: css`
|
view: css`
|
||||||
label: view;
|
label: view;
|
||||||
@ -91,6 +92,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, showSpanFilterMatchesOnly
|
|||||||
`,
|
`,
|
||||||
row: css`
|
row: css`
|
||||||
label: row;
|
label: row;
|
||||||
|
font-size: 0.9em;
|
||||||
&:hover .${spanBarClassName} {
|
&:hover .${spanBarClassName} {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
@ -213,7 +215,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, showSpanFilterMatchesOnly
|
|||||||
outline: none;
|
outline: none;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
margin-right: 8px;
|
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
padding-right: 0.25em;
|
padding-right: 0.25em;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -222,24 +223,17 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, showSpanFilterMatchesOnly
|
|||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
&::before {
|
|
||||||
content: ' ';
|
|
||||||
position: absolute;
|
|
||||||
top: 4px;
|
|
||||||
bottom: 4px;
|
|
||||||
left: 0;
|
|
||||||
border-left: 4px solid;
|
|
||||||
border-left-color: inherit;
|
|
||||||
}
|
|
||||||
&:focus {
|
&:focus {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
&:hover > small {
|
&:hover > span {
|
||||||
color: ${autoColor(theme, '#000')};
|
color: ${autoColor(theme, '#000')};
|
||||||
}
|
}
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-style: solid;
|
||||||
`,
|
`,
|
||||||
nameDetailExpanded: css`
|
nameDetailExpanded: css`
|
||||||
label: nameDetailExpanded;
|
label: nameDetailExpanded;
|
||||||
@ -249,8 +243,9 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, showSpanFilterMatchesOnly
|
|||||||
`,
|
`,
|
||||||
svcName: css`
|
svcName: css`
|
||||||
label: svcName;
|
label: svcName;
|
||||||
padding: 0 0.25rem 0 0.5rem;
|
font-size: 0.9em;
|
||||||
font-size: 1.05em;
|
font-weight: bold;
|
||||||
|
margin-right: 0.25rem;
|
||||||
`,
|
`,
|
||||||
svcNameChildrenCollapsed: css`
|
svcNameChildrenCollapsed: css`
|
||||||
label: svcNameChildrenCollapsed;
|
label: svcNameChildrenCollapsed;
|
||||||
@ -301,6 +296,7 @@ export type SpanBarRowProps = {
|
|||||||
onDetailToggled: (spanID: string) => void;
|
onDetailToggled: (spanID: string) => void;
|
||||||
onChildrenToggled: (spanID: string) => void;
|
onChildrenToggled: (spanID: string) => void;
|
||||||
numTicks: number;
|
numTicks: number;
|
||||||
|
showServiceName: boolean;
|
||||||
rpc?:
|
rpc?:
|
||||||
| {
|
| {
|
||||||
viewStart: number;
|
viewStart: number;
|
||||||
@ -327,6 +323,7 @@ export type SpanBarRowProps = {
|
|||||||
clippingRight?: boolean;
|
clippingRight?: boolean;
|
||||||
createSpanLink?: SpanLinkFunc;
|
createSpanLink?: SpanLinkFunc;
|
||||||
datasourceType: string;
|
datasourceType: string;
|
||||||
|
visibleSpanIds: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -378,6 +375,8 @@ export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
|||||||
theme,
|
theme,
|
||||||
createSpanLink,
|
createSpanLink,
|
||||||
datasourceType,
|
datasourceType,
|
||||||
|
showServiceName,
|
||||||
|
visibleSpanIds,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {
|
const {
|
||||||
duration,
|
duration,
|
||||||
@ -432,6 +431,7 @@ export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
|||||||
hoverIndentGuideIds={hoverIndentGuideIds}
|
hoverIndentGuideIds={hoverIndentGuideIds}
|
||||||
addHoverIndentGuideId={addHoverIndentGuideId}
|
addHoverIndentGuideId={addHoverIndentGuideId}
|
||||||
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
||||||
|
visibleSpanIds={visibleSpanIds}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -440,43 +440,45 @@ export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
|||||||
title={labelDetail}
|
title={labelDetail}
|
||||||
onClick={this._detailToggle}
|
onClick={this._detailToggle}
|
||||||
role="switch"
|
role="switch"
|
||||||
style={{ borderColor: color }}
|
style={{ background: `${color}10`, borderBottomColor: `${color}CF` }}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<span
|
{showErrorIcon && (
|
||||||
className={cx(styles.svcName, {
|
<Icon
|
||||||
[styles.svcNameChildrenCollapsed]: isParent && !isChildrenExpanded,
|
name={'exclamation-circle'}
|
||||||
})}
|
style={{
|
||||||
>
|
backgroundColor: span.errorIconColor
|
||||||
{showErrorIcon && (
|
? autoColor(theme, span.errorIconColor)
|
||||||
<Icon
|
: autoColor(theme, '#db2828'),
|
||||||
name={'exclamation-circle'}
|
}}
|
||||||
style={{
|
className={styles.errorIcon}
|
||||||
backgroundColor: span.errorIconColor
|
/>
|
||||||
? autoColor(theme, span.errorIconColor)
|
)}
|
||||||
: autoColor(theme, '#db2828'),
|
{showServiceName && (
|
||||||
}}
|
<span
|
||||||
className={styles.errorIcon}
|
className={cx(styles.svcName, {
|
||||||
/>
|
[styles.svcNameChildrenCollapsed]: isParent && !isChildrenExpanded,
|
||||||
)}
|
})}
|
||||||
{serviceName}{' '}
|
>
|
||||||
{rpc && (
|
{`${serviceName} `}
|
||||||
<span>
|
</span>
|
||||||
<Icon name={'arrow-right'} />{' '}
|
)}
|
||||||
<i className={styles.rpcColorMarker} style={{ background: rpc.color }} />
|
{rpc && (
|
||||||
{rpc.serviceName}
|
<span>
|
||||||
</span>
|
<Icon name={'arrow-right'} />{' '}
|
||||||
)}
|
<i className={styles.rpcColorMarker} style={{ background: rpc.color }} />
|
||||||
{noInstrumentedServer && (
|
{rpc.serviceName}
|
||||||
<span>
|
</span>
|
||||||
<Icon name={'arrow-right'} />{' '}
|
)}
|
||||||
<i className={styles.rpcColorMarker} style={{ background: noInstrumentedServer.color }} />
|
{noInstrumentedServer && (
|
||||||
{noInstrumentedServer.serviceName}
|
<span>
|
||||||
</span>
|
<Icon name={'arrow-right'} />{' '}
|
||||||
)}
|
<i className={styles.rpcColorMarker} style={{ background: noInstrumentedServer.color }} />
|
||||||
</span>
|
{noInstrumentedServer.serviceName}
|
||||||
<small className={styles.endpointName}>{rpc ? rpc.operationName : operationName}</small>
|
</span>
|
||||||
<small className={styles.endpointName}> {this.getSpanBarLabel(span, spanBarOptions, label)}</small>
|
)}
|
||||||
|
<span className={styles.endpointName}>{rpc ? rpc.operationName : operationName}</span>
|
||||||
|
<span className={styles.endpointName}> {this.getSpanBarLabel(span, spanBarOptions, label)}</span>
|
||||||
</button>
|
</button>
|
||||||
{createSpanLink &&
|
{createSpanLink &&
|
||||||
(() => {
|
(() => {
|
||||||
@ -492,7 +494,7 @@ export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
|||||||
href={links[0].href}
|
href={links[0].href}
|
||||||
// Needs to have target otherwise preventDefault would not work due to angularRouter.
|
// Needs to have target otherwise preventDefault would not work due to angularRouter.
|
||||||
target={'_blank'}
|
target={'_blank'}
|
||||||
style={{ marginRight: '5px' }}
|
style={{ background: `${color}10`, borderBottom: `1px solid ${color}CF`, paddingRight: '4px' }}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
onClick={
|
onClick={
|
||||||
links[0].onClick
|
links[0].onClick
|
||||||
@ -509,7 +511,7 @@ export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
|
|||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
} else if (links && count > 1) {
|
} else if (links && count > 1) {
|
||||||
return <SpanLinksMenu links={links} datasourceType={datasourceType} />;
|
return <SpanLinksMenu links={links} datasourceType={datasourceType} color={color} />;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
&::before {
|
&::before {
|
||||||
border-left: 4px solid;
|
border-left: 1px solid;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
width: 1000px;
|
width: 1000px;
|
||||||
}
|
}
|
||||||
@ -97,6 +97,7 @@ export type SpanDetailRowProps = {
|
|||||||
createFocusSpanLink: (traceId: string, spanId: string) => LinkModel;
|
createFocusSpanLink: (traceId: string, spanId: string) => LinkModel;
|
||||||
topOfViewRefType?: TopOfViewRefType;
|
topOfViewRefType?: TopOfViewRefType;
|
||||||
datasourceType: string;
|
datasourceType: string;
|
||||||
|
visibleSpanIds: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProps> {
|
export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProps> {
|
||||||
@ -134,6 +135,7 @@ export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProp
|
|||||||
createFocusSpanLink,
|
createFocusSpanLink,
|
||||||
topOfViewRefType,
|
topOfViewRefType,
|
||||||
datasourceType,
|
datasourceType,
|
||||||
|
visibleSpanIds,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme);
|
||||||
return (
|
return (
|
||||||
@ -145,6 +147,7 @@ export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProp
|
|||||||
hoverIndentGuideIds={hoverIndentGuideIds}
|
hoverIndentGuideIds={hoverIndentGuideIds}
|
||||||
addHoverIndentGuideId={addHoverIndentGuideId}
|
addHoverIndentGuideId={addHoverIndentGuideId}
|
||||||
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
||||||
|
visibleSpanIds={visibleSpanIds}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
fill="text"
|
fill="text"
|
||||||
|
@ -9,6 +9,7 @@ import { SpanLinkDef } from '../types/links';
|
|||||||
interface SpanLinksProps {
|
interface SpanLinksProps {
|
||||||
links: SpanLinkDef[];
|
links: SpanLinkDef[];
|
||||||
datasourceType: string;
|
datasourceType: string;
|
||||||
|
color: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderMenuItems = (
|
const renderMenuItems = (
|
||||||
@ -46,15 +47,15 @@ const renderMenuItems = (
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpanLinksMenu = ({ links, datasourceType }: SpanLinksProps) => {
|
export const SpanLinksMenu = ({ links, datasourceType, color }: SpanLinksProps) => {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(() => getStyles(color));
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||||
|
|
||||||
const closeMenu = () => setIsMenuOpen(false);
|
const closeMenu = () => setIsMenuOpen(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="SpanLinksMenu">
|
<div data-testid="SpanLinksMenu" className={styles.wrapper}>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
setIsMenuOpen(true);
|
setIsMenuOpen(true);
|
||||||
@ -65,7 +66,7 @@ export const SpanLinksMenu = ({ links, datasourceType }: SpanLinksProps) => {
|
|||||||
}}
|
}}
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
>
|
>
|
||||||
<Icon name="link" className={styles.button} />
|
<Icon name="link" className={styles.icon} />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{isMenuOpen ? (
|
{isMenuOpen ? (
|
||||||
@ -81,13 +82,23 @@ export const SpanLinksMenu = ({ links, datasourceType }: SpanLinksProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = () => {
|
const getStyles = (color: string) => {
|
||||||
return {
|
return {
|
||||||
|
wrapper: css`
|
||||||
|
border: none;
|
||||||
|
background: ${color}10;
|
||||||
|
border-bottom: 1px solid ${color}CF;
|
||||||
|
padding-right: 4px;
|
||||||
|
`,
|
||||||
button: css`
|
button: css`
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0 3px 0 0;
|
`,
|
||||||
|
icon: css`
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
`,
|
`,
|
||||||
menuItem: css`
|
menuItem: css`
|
||||||
max-width: 60ch;
|
max-width: 60ch;
|
||||||
|
@ -39,6 +39,7 @@ describe('SpanTreeOffset', () => {
|
|||||||
addHoverIndentGuideId: jest.fn(),
|
addHoverIndentGuideId: jest.fn(),
|
||||||
hoverIndentGuideIds: new Set(),
|
hoverIndentGuideIds: new Set(),
|
||||||
removeHoverIndentGuideId: jest.fn(),
|
removeHoverIndentGuideId: jest.fn(),
|
||||||
|
visibleSpanIds: [],
|
||||||
span: {
|
span: {
|
||||||
hasChildren: false,
|
hasChildren: false,
|
||||||
spanID: ownSpanID,
|
spanID: ownSpanID,
|
||||||
|
@ -40,10 +40,10 @@ export const getStyles = stylesFactory((theme: GrafanaTheme2) => {
|
|||||||
indentGuide: css`
|
indentGuide: css`
|
||||||
label: indentGuide;
|
label: indentGuide;
|
||||||
/* The size of the indentGuide is based off of the iconWrapper */
|
/* The size of the indentGuide is based off of the iconWrapper */
|
||||||
padding-right: calc(0.5rem + 12px);
|
padding-right: 1rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-left: 3px solid transparent;
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
transition: padding 300ms ease-out;
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: '';
|
||||||
padding-left: 1px;
|
padding-left: 1px;
|
||||||
@ -52,15 +52,17 @@ export const getStyles = stylesFactory((theme: GrafanaTheme2) => {
|
|||||||
`,
|
`,
|
||||||
indentGuideActive: css`
|
indentGuideActive: css`
|
||||||
label: indentGuideActive;
|
label: indentGuideActive;
|
||||||
border-color: ${autoColor(theme, 'darkgrey')};
|
|
||||||
&::before {
|
&::before {
|
||||||
background-color: transparent;
|
background-color: ${autoColor(theme, '#777')};
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
indentGuideThin: css`
|
||||||
|
padding-right: 0.3rem;
|
||||||
|
`,
|
||||||
iconWrapper: css`
|
iconWrapper: css`
|
||||||
label: iconWrapper;
|
label: iconWrapper;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0.25rem;
|
right: 0;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -75,6 +77,7 @@ export type TProps = {
|
|||||||
addHoverIndentGuideId: (spanID: string) => void;
|
addHoverIndentGuideId: (spanID: string) => void;
|
||||||
removeHoverIndentGuideId: (spanID: string) => void;
|
removeHoverIndentGuideId: (spanID: string) => void;
|
||||||
theme: GrafanaTheme2;
|
theme: GrafanaTheme2;
|
||||||
|
visibleSpanIds: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export class UnthemedSpanTreeOffset extends React.PureComponent<TProps> {
|
export class UnthemedSpanTreeOffset extends React.PureComponent<TProps> {
|
||||||
@ -133,25 +136,28 @@ export class UnthemedSpanTreeOffset extends React.PureComponent<TProps> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { childrenVisible, onClick, showChildrenIcon, span, theme } = this.props;
|
const { childrenVisible, onClick, showChildrenIcon, span, theme, visibleSpanIds } = this.props;
|
||||||
const { hasChildren, spanID } = span;
|
const { hasChildren, spanID } = span;
|
||||||
const wrapperProps = hasChildren ? { onClick, role: 'switch', 'aria-checked': childrenVisible } : null;
|
const wrapperProps = hasChildren ? { onClick, role: 'switch', 'aria-checked': childrenVisible } : null;
|
||||||
const icon =
|
const icon =
|
||||||
showChildrenIcon &&
|
showChildrenIcon &&
|
||||||
hasChildren &&
|
hasChildren &&
|
||||||
(childrenVisible ? (
|
(childrenVisible ? (
|
||||||
<Icon name={'angle-down'} data-testid="icon-arrow-down" />
|
<Icon name={'angle-down'} data-testid="icon-arrow-down" size={'sm'} />
|
||||||
) : (
|
) : (
|
||||||
<Icon name={'angle-right'} data-testid="icon-arrow-right" />
|
<Icon name={'angle-right'} data-testid="icon-arrow-right" size={'sm'} />
|
||||||
));
|
));
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={cx(styles.SpanTreeOffset, { [styles.SpanTreeOffsetParent]: hasChildren })} {...wrapperProps}>
|
<span className={cx(styles.SpanTreeOffset, { [styles.SpanTreeOffsetParent]: hasChildren })} {...wrapperProps}>
|
||||||
{this.ancestorIds.map((ancestorId) => (
|
{this.ancestorIds.map((ancestorId, index) => (
|
||||||
<span
|
<span
|
||||||
key={ancestorId}
|
key={ancestorId}
|
||||||
className={cx(styles.indentGuide, {
|
className={cx(styles.indentGuide, {
|
||||||
[styles.indentGuideActive]: this.props.hoverIndentGuideIds.has(ancestorId),
|
[styles.indentGuideActive]: this.props.hoverIndentGuideIds.has(ancestorId),
|
||||||
|
[styles.indentGuideThin]:
|
||||||
|
index !== this.ancestorIds.length - 1 && ancestorId !== 'root' && !visibleSpanIds.includes(ancestorId),
|
||||||
})}
|
})}
|
||||||
data-ancestor-id={ancestorId}
|
data-ancestor-id={ancestorId}
|
||||||
data-testid="SpanTreeOffset--indentGuide"
|
data-testid="SpanTreeOffset--indentGuide"
|
||||||
|
@ -122,6 +122,7 @@ export const DEFAULT_HEIGHTS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const NUM_TICKS = 5;
|
const NUM_TICKS = 5;
|
||||||
|
const BUFFER_SIZE = 33;
|
||||||
|
|
||||||
function generateRowStates(
|
function generateRowStates(
|
||||||
spans: TraceSpan[] | TNil,
|
spans: TraceSpan[] | TNil,
|
||||||
@ -331,9 +332,15 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
|
|
||||||
renderRow = (key: string, style: React.CSSProperties, index: number, attrs: {}) => {
|
renderRow = (key: string, style: React.CSSProperties, index: number, attrs: {}) => {
|
||||||
const { isDetail, span, spanIndex } = this.getRowStates()[index];
|
const { isDetail, span, spanIndex } = this.getRowStates()[index];
|
||||||
|
|
||||||
|
// Compute the list of currently visible span IDs to pass to the row renderers.
|
||||||
|
const start = Math.max((this.listView?.getTopVisibleIndex() || 0) - BUFFER_SIZE, 0);
|
||||||
|
const end = (this.listView?.getBottomVisibleIndex() || 0) + BUFFER_SIZE;
|
||||||
|
const visibleSpanIds = this.getVisibleSpanIds(start, end);
|
||||||
|
|
||||||
return isDetail
|
return isDetail
|
||||||
? this.renderSpanDetailRow(span, key, style, attrs)
|
? this.renderSpanDetailRow(span, key, style, attrs, visibleSpanIds)
|
||||||
: this.renderSpanBarRow(span, spanIndex, key, style, attrs);
|
: this.renderSpanBarRow(span, spanIndex, key, style, attrs, visibleSpanIds);
|
||||||
};
|
};
|
||||||
|
|
||||||
scrollToSpan = (headerHeight: number, spanID?: string) => {
|
scrollToSpan = (headerHeight: number, spanID?: string) => {
|
||||||
@ -346,7 +353,14 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
renderSpanBarRow(span: TraceSpan, spanIndex: number, key: string, style: React.CSSProperties, attrs: {}) {
|
renderSpanBarRow(
|
||||||
|
span: TraceSpan,
|
||||||
|
spanIndex: number,
|
||||||
|
key: string,
|
||||||
|
style: React.CSSProperties,
|
||||||
|
attrs: {},
|
||||||
|
visibleSpanIds: string[]
|
||||||
|
) {
|
||||||
const { spanID } = span;
|
const { spanID } = span;
|
||||||
const { serviceName } = span.process;
|
const { serviceName } = span.process;
|
||||||
const {
|
const {
|
||||||
@ -406,6 +420,8 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prevSpan = spanIndex > 0 ? trace.spans[spanIndex - 1] : null;
|
||||||
|
|
||||||
const styles = getStyles(this.props);
|
const styles = getStyles(this.props);
|
||||||
return (
|
return (
|
||||||
<div className={styles.row} key={key} style={style} {...attrs}>
|
<div className={styles.row} key={key} style={style} {...attrs}>
|
||||||
@ -434,12 +450,14 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
||||||
createSpanLink={createSpanLink}
|
createSpanLink={createSpanLink}
|
||||||
datasourceType={datasourceType}
|
datasourceType={datasourceType}
|
||||||
|
showServiceName={prevSpan === null || prevSpan.process.serviceName !== span.process.serviceName}
|
||||||
|
visibleSpanIds={visibleSpanIds}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSpanDetailRow(span: TraceSpan, key: string, style: React.CSSProperties, attrs: {}) {
|
renderSpanDetailRow(span: TraceSpan, key: string, style: React.CSSProperties, attrs: {}, visibleSpanIds: string[]) {
|
||||||
const { spanID } = span;
|
const { spanID } = span;
|
||||||
const { serviceName } = span.process;
|
const { serviceName } = span.process;
|
||||||
const {
|
const {
|
||||||
@ -473,6 +491,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
}
|
}
|
||||||
const color = getColorByKey(serviceName, theme);
|
const color = getColorByKey(serviceName, theme);
|
||||||
const styles = getStyles(this.props);
|
const styles = getStyles(this.props);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.row} key={key} style={{ ...style, zIndex: 1 }} {...attrs}>
|
<div className={styles.row} key={key} style={{ ...style, zIndex: 1 }} {...attrs}>
|
||||||
<SpanDetailRow
|
<SpanDetailRow
|
||||||
@ -500,6 +519,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
createFocusSpanLink={createFocusSpanLink}
|
createFocusSpanLink={createFocusSpanLink}
|
||||||
topOfViewRefType={topOfViewRefType}
|
topOfViewRefType={topOfViewRefType}
|
||||||
datasourceType={datasourceType}
|
datasourceType={datasourceType}
|
||||||
|
visibleSpanIds={visibleSpanIds}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -516,9 +536,21 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getVisibleSpanIds = memoizeOne((start: number, end: number) => {
|
||||||
|
const spanIds = [];
|
||||||
|
for (let i = start; i < end; i++) {
|
||||||
|
const rowState = this.getRowStates()[i];
|
||||||
|
if (rowState?.span) {
|
||||||
|
spanIds.push(rowState.span.spanID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return spanIds;
|
||||||
|
});
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const styles = getStyles(this.props);
|
const styles = getStyles(this.props);
|
||||||
const { scrollElement } = this.props;
|
const { scrollElement } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ListView
|
<ListView
|
||||||
@ -526,8 +558,8 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
dataLength={this.getRowStates().length}
|
dataLength={this.getRowStates().length}
|
||||||
itemHeightGetter={this.getRowHeight}
|
itemHeightGetter={this.getRowHeight}
|
||||||
itemRenderer={this.renderRow}
|
itemRenderer={this.renderRow}
|
||||||
viewBuffer={50}
|
viewBuffer={BUFFER_SIZE}
|
||||||
viewBufferMin={50}
|
viewBufferMin={BUFFER_SIZE}
|
||||||
itemsWrapperClassName={styles.rowsWrapper}
|
itemsWrapperClassName={styles.rowsWrapper}
|
||||||
getKeyFromIndex={this.getKeyFromIndex}
|
getKeyFromIndex={this.getKeyFromIndex}
|
||||||
getIndexFromKey={this.getIndexFromKey}
|
getIndexFromKey={this.getIndexFromKey}
|
||||||
|
Loading…
Reference in New Issue
Block a user