diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.test.tsx
index 09ceafa6391..597b6d125c6 100644
--- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.test.tsx
+++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.test.tsx
@@ -96,9 +96,7 @@ describe('AccordianKeyValues test', () => {
it('renders the summary instead of the table when it is not expanded', () => {
setupAccordian({ isOpen: false } as AccordianKeyValuesProps);
- expect(
- screen.getByRole('switch', { name: 'test accordian: span.kind = client omg = mos-def' })
- ).toBeInTheDocument();
+ expect(screen.getByRole('switch', { name: 'test accordian: span.kind client omg mos-def' })).toBeInTheDocument();
expect(screen.queryByRole('table')).not.toBeInTheDocument();
expect(screen.queryAllByRole('cell')).toHaveLength(0);
});
diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx
index b182fa5df41..d8d621cf218 100644
--- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx
+++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx
@@ -68,7 +68,6 @@ export const getStyles = (theme: GrafanaTheme2) => {
label: 'summaryItem',
display: 'inline',
paddingRight: '0.5rem',
- borderRight: `1px solid ${autoColor(theme, '#ddd')}`,
'&:last-child': {
paddingRight: 0,
borderRight: 'none',
@@ -77,11 +76,7 @@ export const getStyles = (theme: GrafanaTheme2) => {
summaryLabel: css({
label: 'summaryLabel',
color: autoColor(theme, '#777'),
- }),
- summaryDelim: css({
- label: 'summaryDelim',
- color: autoColor(theme, '#bbb'),
- padding: '0 0.2em',
+ paddingRight: '0.5rem',
}),
};
};
@@ -116,7 +111,6 @@ export function KeyValuesSummary({ data = null }: KeyValuesSummaryProps) {
// `i` is necessary in the key because item.key can repeat
{item.key}
- =
{String(item.value)}
))}
diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.test.tsx
index f69f3c8808f..18085f3fdcb 100644
--- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.test.tsx
+++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.test.tsx
@@ -56,7 +56,7 @@ describe('AccordianLogs tests', () => {
it('shows the number of log entries', () => {
setup();
- expect(screen.getByRole('switch', { name: 'Events (2)' })).toBeInTheDocument();
+ expect(screen.getByRole('switch', { name: 'Events 2' })).toBeInTheDocument();
});
it('hides log entries when not expanded', () => {
@@ -103,12 +103,10 @@ describe('AccordianLogs tests', () => {
setup({ isOpen: true, openedItems: new Set() } as AccordianLogsProps);
expect(
screen.getByRole('switch', {
- name: '15μs (foo event name) : message = oh the next log message more = stuff',
+ name: '15μs (foo event name) : message oh the next log message more stuff',
})
).toBeInTheDocument();
- expect(
- screen.getByRole('switch', { name: '5μs: message = oh the log message something = else' })
- ).toBeInTheDocument();
+ expect(screen.getByRole('switch', { name: '5μs: message oh the log message something else' })).toBeInTheDocument();
});
it('renders event name and duration when events list is open', () => {
diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx
index 1de9660cbb6..5584760b04b 100644
--- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx
+++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx
@@ -18,7 +18,7 @@ import * as React from 'react';
import { GrafanaTheme2, TraceLog } from '@grafana/data';
import { Trans } from '@grafana/i18n';
-import { Icon, useStyles2 } from '@grafana/ui';
+import { Counter, Icon, useStyles2 } from '@grafana/ui';
import { autoColor } from '../../Theme';
import { formatDuration } from '../utils';
@@ -31,24 +31,22 @@ const getStyles = (theme: GrafanaTheme2) => {
return {
AccordianLogs: css({
label: 'AccordianLogs',
- border: `1px solid ${autoColor(theme, '#d8d8d8')}`,
position: 'relative',
marginBottom: '0.25rem',
}),
AccordianLogsHeader: css({
label: 'AccordianLogsHeader',
- background: autoColor(theme, '#e4e4e4'),
color: 'inherit',
- display: 'block',
- padding: '0.25rem 0.5rem',
+ display: 'flex',
+ alignItems: 'center',
+ padding: '0.25rem 0.1em',
'&:hover': {
- background: autoColor(theme, '#dadada'),
+ background: autoColor(theme, '#e8e8e8'),
},
}),
AccordianLogsContent: css({
label: 'AccordianLogsContent',
background: autoColor(theme, '#f0f0f0'),
- borderTop: `1px solid ${autoColor(theme, '#d8d8d8')}`,
padding: '0.5rem 0.5rem 0.25rem 0.5rem',
}),
AccordianLogsFooter: css({
@@ -90,7 +88,7 @@ export default function AccordianLogs({
arrow = isOpen ? (
) : (
-
+
);
HeaderComponent = 'a';
headerProps = {
@@ -108,7 +106,7 @@ export default function AccordianLogs({
Events
{' '}
- ({logs.length})
+
{isOpen && (
diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx
index 0b8065b09c6..b9735b03720 100644
--- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx
+++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx
@@ -28,12 +28,13 @@ import {
TraceLog,
PluginExtensionResourceAttributesContext,
PluginExtensionPoints,
+ IconName,
} from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend';
import { usePluginLinks } from '@grafana/runtime';
import { TimeZone } from '@grafana/schema';
-import { Divider, Icon, TextArea, useStyles2 } from '@grafana/ui';
+import { Icon, TextArea, useStyles2 } from '@grafana/ui';
import { pyroscopeProfileIdTagKey } from '../../../createSpanLink';
import { autoColor } from '../../Theme';
@@ -159,9 +160,6 @@ const getStyles = (theme: GrafanaTheme2) => {
label: 'AccordianWarningsLabel',
color: autoColor(theme, '#d36c08'),
}),
- AccordianKeyValuesItem: css({
- marginBottom: theme.spacing(0.5),
- }),
Textarea: css({
wordBreak: 'break-all',
whiteSpace: 'pre',
@@ -173,6 +171,7 @@ const getStyles = (theme: GrafanaTheme2) => {
display: 'flex',
flexWrap: 'wrap',
gap: '10px',
+ marginBottom: theme.spacing(2),
}),
};
};
@@ -186,6 +185,7 @@ export type TraceFlameGraphs = {
};
export type SpanDetailProps = {
+ color: string;
detailState: DetailState;
logItemToggle: (spanID: string, log: TraceLog) => void;
logsToggle: (spanID: string) => void;
@@ -215,6 +215,7 @@ export type SpanDetailProps = {
export default function SpanDetail(props: SpanDetailProps) {
const {
+ color,
detailState,
logItemToggle,
logsToggle,
@@ -263,6 +264,9 @@ export default function SpanDetail(props: SpanDetailProps) {
} = span;
const { timeZone } = props;
+ const durationIcon: IconName = 'hourglass';
+ const startIcon: IconName = 'clock-nine';
+
let overviewItems = [
{
key: 'svc',
@@ -273,11 +277,13 @@ export default function SpanDetail(props: SpanDetailProps) {
key: 'duration',
label: t('explore.span-detail.overview-items.label.duration', 'Duration:'),
value: formatDuration(duration),
+ icon: durationIcon,
},
{
key: 'start',
label: t('explore.span-detail.overview-items.label.start-time', 'Start Time:'),
value: formatDuration(relativeStartTime) + getAbsoluteTime(startTime, timeZone),
+ icon: startIcon,
},
...(span.childSpanCount > 0
? [
@@ -353,11 +359,10 @@ export default function SpanDetail(props: SpanDetailProps) {
{operationName}
-
+
{linksComponent}
-
{process.tags && (
{
expect(() => setup()).not.toThrow();
});
- it('calls toggle on click', async () => {
- const mockToggle = jest.fn();
- setup({ onDetailToggled: mockToggle } as unknown as SpanDetailRowProps);
- expect(mockToggle).not.toHaveBeenCalled();
-
- const detailRow = screen.getByTestId('detail-row-expanded-accent');
- await userEvent.click(detailRow);
- expect(mockToggle).toHaveBeenCalled();
- });
-
- it('renders the span tree offset', () => {
- setup();
-
- expect(screen.getByTestId('SpanTreeOffset--indentGuide')).toBeInTheDocument();
- });
-
it('renders the SpanDetail', () => {
setup();
diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.tsx
index a6e8219214d..201f0570b27 100644
--- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.tsx
+++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.tsx
@@ -13,15 +13,13 @@
// limitations under the License.
import { css } from '@emotion/css';
-import classNames from 'classnames';
import { PureComponent } from 'react';
import { CoreApp, GrafanaTheme2, LinkModel, TimeRange, TraceLog } from '@grafana/data';
import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend';
import { TimeZone } from '@grafana/schema';
-import { Button, clearButtonStyles, stylesFactory, withTheme2 } from '@grafana/ui';
+import { stylesFactory, withTheme2 } from '@grafana/ui';
-import { autoColor } from '../Theme';
import { SpanLinkFunc } from '../types/links';
import { TraceSpan, TraceSpanReference } from '../types/trace';
@@ -64,10 +62,22 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => {
}),
infoWrapper: css({
label: 'infoWrapper',
- border: `1px solid ${autoColor(theme, '#d3d3d3')}`,
- borderTop: '3px solid',
padding: '0.75rem',
}),
+ cell: css({
+ label: 'cell',
+ display: 'flex !important',
+ width: '100% !important',
+ }),
+ indentSpacer: css({
+ label: 'indentSpacer',
+ flex: 'none',
+ }),
+ detailWrapper: css({
+ label: 'detailWrapper',
+ flex: '1',
+ minWidth: 0,
+ }),
};
});
@@ -115,7 +125,6 @@ export class UnthemedSpanDetailRow extends PureComponent {
render() {
const {
color,
- columnDivision,
detailState,
logItemToggle,
logsToggle,
@@ -131,72 +140,68 @@ export class UnthemedSpanDetailRow extends PureComponent {
traceStartTime,
traceDuration,
traceName,
- hoverIndentGuideIds,
- addHoverIndentGuideId,
- removeHoverIndentGuideId,
theme,
createSpanLink,
focusedSpanId,
createFocusSpanLink,
datasourceType,
datasourceUid,
- visibleSpanIds,
traceFlameGraphs,
setTraceFlameGraphs,
setRedrawListView,
timeRange,
app,
+ hoverIndentGuideIds,
+ addHoverIndentGuideId,
+ removeHoverIndentGuideId,
+ visibleSpanIds,
} = this.props;
const styles = getStyles(theme);
return (
-
-
-
-
-
-
-
+
+
+
);
diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.tsx
index 5ad66c10762..1d8b3c356ff 100644
--- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.tsx
+++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/TimelineHeaderRow/TimelineCollapser.tsx
@@ -14,10 +14,11 @@
import { css } from '@emotion/css';
+import { GrafanaTheme2 } from '@grafana/data';
import { t } from '@grafana/i18n';
-import { IconButton, useStyles2 } from '@grafana/ui';
+import { Button, useStyles2 } from '@grafana/ui';
-const getStyles = () => ({
+const getStyles = (theme: GrafanaTheme2) => ({
TimelineCollapser: css({
alignItems: 'center',
display: 'flex',
@@ -25,6 +26,16 @@ const getStyles = () => ({
justifyContent: 'center',
marginRight: '0.5rem',
}),
+ buttonsContainer: css({
+ display: 'flex',
+ flexDirection: 'row',
+ gap: '0.5rem',
+ paddingRight: theme.spacing(1),
+ }),
+ buttonContainer: css({
+ display: 'flex',
+ alignItems: 'center',
+ }),
});
type CollapserProps = {
@@ -40,34 +51,52 @@ export function TimelineCollapser(props: CollapserProps) {
return (
);
}
diff --git a/public/app/features/explore/TraceView/components/common/LabeledList.tsx b/public/app/features/explore/TraceView/components/common/LabeledList.tsx
index 8b8156168de..b45e49b4cfa 100644
--- a/public/app/features/explore/TraceView/components/common/LabeledList.tsx
+++ b/public/app/features/explore/TraceView/components/common/LabeledList.tsx
@@ -16,8 +16,8 @@ import { css } from '@emotion/css';
import cx from 'classnames';
import * as React from 'react';
-import { GrafanaTheme2 } from '@grafana/data';
-import { useStyles2 } from '@grafana/ui';
+import { GrafanaTheme2, IconName } from '@grafana/data';
+import { Icon, useStyles2 } from '@grafana/ui';
import { autoColor } from '../Theme';
@@ -45,7 +45,7 @@ const getStyles = (divider: boolean) => (theme: GrafanaTheme2) => {
borderRight: `1px solid ${autoColor(theme, '#ddd')}`,
padding: '0 8px',
}
- : {}),
+ : { padding: '0 4px' }),
}),
LabeledListLabel: css({
label: 'LabeledListLabel',
@@ -56,24 +56,44 @@ const getStyles = (divider: boolean) => (theme: GrafanaTheme2) => {
label: 'LabeledListValue',
marginRight: divider ? undefined : '0.55rem',
}),
+ LabeledListIcon: css({
+ label: 'LabeledListIcon',
+ marginRight: '0.25rem',
+ marginTop: '-0.1rem',
+ }),
+ LabeledListServiceLine: css({
+ label: 'LabeledListServiceLine',
+ display: 'inline-block',
+ width: '1.25rem',
+ height: '0.35rem',
+ marginRight: '0.5rem',
+ verticalAlign: 'middle',
+ borderRadius: theme.shape.radius.default,
+ }),
};
};
type LabeledListProps = {
className?: string;
divider?: boolean;
- items: Array<{ key: string; label: React.ReactNode; value: React.ReactNode }>;
+ items: Array<{ key: string; label: React.ReactNode; value: React.ReactNode; icon?: IconName }>;
+ color?: string;
};
export default function LabeledList(props: LabeledListProps) {
- const { className, divider = false, items } = props;
+ const { className, divider = false, items, color } = props;
const styles = useStyles2(getStyles(divider));
return (
- {items.map(({ key, label, value }) => {
+ {items.map(({ key, label, value, icon }) => {
return (
+ // If label is service, create small line on left with color
+ {label === 'Service:' && (
+
+ )}
+ {icon && }
{label}
{value}