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 597b6d125c6..908126b649b 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,7 +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 d8d621cf218..93822458ac7 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx @@ -17,7 +17,7 @@ import cx from 'classnames'; import * as React from 'react'; import { GrafanaTheme2, TraceKeyValuePair } from '@grafana/data'; -import { Icon, useStyles2 } from '@grafana/ui'; +import { Counter, Icon, useStyles2 } from '@grafana/ui'; import { autoColor } from '../../Theme'; import TNil from '../../types/TNil'; @@ -43,6 +43,10 @@ export const getStyles = (theme: GrafanaTheme2) => { background: autoColor(theme, '#e8e8e8'), }, }), + headerLabel: css({ + width: '120px', + display: 'inline-block', + }), headerEmpty: css({ label: 'headerEmpty', background: 'none', @@ -87,6 +91,9 @@ export type AccordianKeyValuesProps = { logName?: string; highContrast?: boolean; interactive?: boolean; + onlyValues?: boolean; + showSummary?: boolean; + showCountBadge?: boolean; isOpen: boolean; label: string | React.ReactNode; linksGetter?: ((pairs: TraceKeyValuePair[], index: number) => KeyValuesTableLink[]) | TNil; @@ -127,6 +134,9 @@ export default function AccordianKeyValues({ isOpen, label, linksGetter, + onlyValues = false, + showSummary = true, + showCountBadge = false, onToggle = null, }: AccordianKeyValuesProps) { const isEmpty = (!Array.isArray(data) || !data.length) && !logName; @@ -148,7 +158,7 @@ export default function AccordianKeyValues({ }; } - const showDataSummaryFields = data.length > 0 && !isOpen; + const showDataSummaryFields = showSummary && data.length > 0 && !isOpen; return (
@@ -161,9 +171,9 @@ export default function AccordianKeyValues({ data-testid="AccordianKeyValues--header" > {arrow} - + {label} - {showDataSummaryFields && ':'} + {showCountBadge ? : null} {showDataSummaryFields && ( @@ -171,7 +181,7 @@ export default function AccordianKeyValues({ )}
- {isOpen && } + {isOpen && } ); } 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 18085f3fdcb..19ff85b2dd7 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 @@ -103,10 +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 5584760b04b..b708b7f3ec8 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx @@ -32,14 +32,12 @@ const getStyles = (theme: GrafanaTheme2) => { AccordianLogs: css({ label: 'AccordianLogs', position: 'relative', - marginBottom: '0.25rem', }), AccordianLogsHeader: css({ label: 'AccordianLogsHeader', color: 'inherit', display: 'flex', alignItems: 'center', - padding: '0.25rem 0.1em', '&:hover': { background: autoColor(theme, '#e8e8e8'), }, diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.test.tsx index d7296055bfc..14fa5187396 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.test.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.test.tsx @@ -75,7 +75,7 @@ describe('AccordianReferences tests', () => { it('renders the correct number of references', () => { setup(); - expect(screen.getByRole('switch', { name: 'References (3)' })).toBeInTheDocument(); + expect(screen.getByRole('switch', { name: 'References 3' })).toBeInTheDocument(); }); it('content doesnt show when not expanded', () => { @@ -88,7 +88,7 @@ describe('AccordianReferences tests', () => { it('renders the content when it is expanded', () => { setup({ isOpen: true } as AccordianReferencesProps); - expect(screen.getByRole('switch', { name: 'References (3)' })).toBeInTheDocument(); + expect(screen.getByRole('switch', { name: 'References 3' })).toBeInTheDocument(); expect(screen.getAllByRole('link', { name: /^service\d\sop\d/ })).toHaveLength(2); expect(screen.getByRole('link', { name: /^View\sLinked/ })).toBeInTheDocument(); }); diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx index de60443707e..10c31897ec6 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx @@ -17,7 +17,7 @@ import * as React from 'react'; import { Field, GrafanaTheme2, LinkModel } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; -import { Icon, useStyles2 } from '@grafana/ui'; +import { Counter, Icon, useStyles2 } from '@grafana/ui'; import { autoColor } from '../../Theme'; import { TraceSpanReference } from '../../types/trace'; @@ -36,16 +36,14 @@ const getStyles = (theme: GrafanaTheme2) => ({ }), AccordianReferences: css({ label: 'AccordianReferences', - border: `1px solid ${autoColor(theme, '#d8d8d8')}`, position: 'relative', marginBottom: '0.25rem', }), AccordianReferencesHeader: css({ label: 'AccordianReferencesHeader', - background: autoColor(theme, '#e4e4e4'), color: 'inherit', display: 'block', - padding: '0.25rem 0.5rem', + padding: '0.25rem 0', '&:hover': { background: autoColor(theme, '#dadada'), }, @@ -223,7 +221,7 @@ const AccordianReferences = ({ References {' '} - ({data.length}) + {isOpen && ( ', () => { - const props = { - compact: false, - data: warnings, - highContrast: false, - isOpen: false, - label: 'le-label', - onToggle: jest.fn(), - }; - - it('renders without exploding', () => { - render(); - expect(() => render()).not.toThrow(); - }); - - it('renders the label', () => { - render(); - const { getByText } = within(screen.getByTestId('AccordianText--header')); - expect(getByText(props.label)).toBeInTheDocument(); - }); - - it('renders the content when it is expanded', () => { - props.isOpen = true; - render(); - warnings.forEach((warning) => { - expect(screen.getByText(warning)).toBeInTheDocument(); - }); - }); -}); diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianText.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianText.tsx deleted file mode 100644 index f0efc64b10a..00000000000 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianText.tsx +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2019 Uber Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { css } from '@emotion/css'; -import cx from 'classnames'; -import * as React from 'react'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { Icon, useStyles2 } from '@grafana/ui'; - -import { autoColor } from '../../Theme'; -import TNil from '../../types/TNil'; - -import { getStyles as getAccordianKeyValuesStyles } from './AccordianKeyValues'; -import TextList from './TextList'; - -import { alignIcon } from '.'; - -const getStyles = (theme: GrafanaTheme2) => ({ - header: css({ - cursor: 'pointer', - overflow: 'hidden', - padding: '0.25em 0.1em', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - '&:hover': { - background: autoColor(theme, '#e8e8e8'), - }, - }), -}); - -type AccordianTextProps = { - className?: string | TNil; - headerClassName?: string | TNil; - data: string[]; - highContrast?: boolean; - interactive?: boolean; - isOpen: boolean; - label: React.ReactNode | string; - onToggle?: null | (() => void); - TextComponent?: React.ElementType<{ data: string[] }>; -}; - -function DefaultTextComponent({ data }: { data: string[] }) { - return ; -} - -export default function AccordianText({ - className = null, - data, - headerClassName, - highContrast = false, - interactive = true, - isOpen, - label, - onToggle = null, - TextComponent = DefaultTextComponent, -}: AccordianTextProps) { - const isEmpty = !Array.isArray(data) || !data.length; - const accordianKeyValuesStyles = useStyles2(getAccordianKeyValuesStyles); - const iconCls = cx(alignIcon, { [accordianKeyValuesStyles.emptyIcon]: isEmpty }); - let arrow: React.ReactNode | null = null; - let headerProps: {} | null = null; - if (interactive) { - arrow = isOpen ? ( - - ) : ( - - ); - headerProps = { - 'aria-checked': isOpen, - onClick: isEmpty ? null : onToggle, - role: 'switch', - }; - } - const styles = useStyles2(getStyles); - return ( -
-
- {arrow} - {label} ({data.length}) -
- {isOpen && } -
- ); -} diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx index 83632c9351d..a6c79b2d2cf 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx @@ -47,7 +47,7 @@ export const getStyles = (theme: GrafanaTheme2) => { row: css({ label: 'row', '& > td': { - padding: '0.5rem 0.5rem', + padding: '0 0.5rem', height: '30px', }, '&:nth-child(2n) > td': { @@ -68,7 +68,6 @@ export const getStyles = (theme: GrafanaTheme2) => { color: autoColor(theme, '#888'), whiteSpace: 'pre', width: '125px', - verticalAlign: 'top', }), copyColumn: css({ label: 'copyColumn', @@ -118,19 +117,32 @@ export const LinkValue = ({ link, children }: PropsWithChildren) export type KeyValuesTableProps = { data: TraceKeyValuePair[]; linksGetter?: ((pairs: TraceKeyValuePair[], index: number) => KeyValuesTableLink[]) | TNil; + onlyValues?: boolean; }; export default function KeyValuesTable(props: KeyValuesTableProps) { - const { data, linksGetter } = props; + const { data, linksGetter, onlyValues } = props; const styles = useStyles2(getStyles); return (
{data.map((row, i) => { - const markup = { - __html: jsonMarkup(parseIfComplexJson(row.value)), - }; + let markup = { __html: '' }; + if (row.type === 'code') { + markup = { + __html: `
${row.value}
`, + }; + } else if (row.type === 'text') { + markup = { + __html: `${row.value}`, + }; + } else { + markup = { + __html: jsonMarkup(parseIfComplexJson(row.value)), + }; + } + const jsonTable =
; const links = linksGetter?.(data, i); let valueMarkup; @@ -147,15 +159,17 @@ export default function KeyValuesTable(props: KeyValuesTableProps) { return ( // `i` is necessary in the key because row.key can repeat
- + {!onlyValues && ( + + )} diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/TextList.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/TextList.tsx index f59b1fb4421..29ee78fb866 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/TextList.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/TextList.tsx @@ -15,9 +15,10 @@ import { css } from '@emotion/css'; import cx from 'classnames'; +import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '@grafana/ui'; -const getStyles = () => ({ +const getStyles = (theme: GrafanaTheme2) => ({ TextList: css({ maxHeight: '450px', overflow: 'auto', @@ -32,7 +33,7 @@ const getStyles = () => ({ padding: '0.25rem 0.5rem', verticalAlign: 'top', '&:nth-child(2n)': { - background: '#f5f5f5', + background: theme.colors.background.secondary, }, }), }); 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 67a6043f397..627e8fc2ec4 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx @@ -29,11 +29,11 @@ import { PluginExtensionPoints, IconName, } from '@grafana/data'; -import { Trans, t } from '@grafana/i18n'; +import { t } from '@grafana/i18n'; import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; import { usePluginLinks } from '@grafana/runtime'; import { TimeZone } from '@grafana/schema'; -import { TextArea, useStyles2 } from '@grafana/ui'; +import { useStyles2 } from '@grafana/ui'; import { pyroscopeProfileIdTagKey } from '../../../createSpanLink'; import { autoColor } from '../../Theme'; @@ -46,7 +46,6 @@ import { formatDuration } from '../utils'; import AccordianKeyValues from './AccordianKeyValues'; import AccordianLogs from './AccordianLogs'; import AccordianReferences from './AccordianReferences'; -import AccordianText from './AccordianText'; import DetailState from './DetailState'; import { ShareSpanButton } from './ShareSpanButton'; import { getSpanDetailLinkButtons } from './SpanDetailLinkButtons'; @@ -99,6 +98,9 @@ const getStyles = (theme: GrafanaTheme2) => { gap: '0 1rem', marginBottom: '0.25rem', }), + content: css({ + fontSize: theme.typography.bodySmall.fontSize, + }), listWrapper: css({ overflow: 'hidden', flexGrow: 1, @@ -340,7 +342,7 @@ export default function SpanDetail(props: SpanDetailProps) {
{linksComponent}
-
+
)} + {warnings && warnings.length > 0 && ( - - Warnings - - } - data={warnings} + ({ + key: '', + value: warning, + type: 'text', + }))} + showSummary={false} + showCountBadge={true} isOpen={isWarningsOpen} + onlyValues={true} onToggle={() => warningsToggle(spanID)} + label={t('explore.span-detail.warnings', 'Warnings')} /> )} + {stackTraces?.length ? ( - ({ + key: '', + value: stackTrace, + type: 'code', + }))} + onlyValues={true} + showSummary={false} + showCountBadge={true} isOpen={isStackTracesOpen} - TextComponent={(textComponentProps) => { - let text; - if (textComponentProps.data?.length > 1) { - text = textComponentProps.data - .map((stackTrace, index) => `StackTrace ${index + 1}:\n${stackTrace}`) - .join('\n'); - } else { - text = textComponentProps.data?.[0]; - } - return ( -
- {row.key} - + {row.key} + {valueMarkup}