Skip to content

Commit 238c36f

Browse files
authored
feat: Improve display of large sizes and volumes of highlighted attributes (#1442)
1 parent ff42220 commit 238c36f

File tree

4 files changed

+99
-47
lines changed

4 files changed

+99
-47
lines changed

.changeset/yellow-owls-sin.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": patch
3+
---
4+
5+
feat: Improve display of large sizes and volumes of highlighted attributes

packages/app/src/components/DBHighlightedAttributesList.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { useContext, useMemo } from 'react';
1+
import { useContext, useMemo, useState } from 'react';
22
import { TSource } from '@hyperdx/common-utils/dist/types';
3-
import { Flex } from '@mantine/core';
3+
import { Anchor, Flex } from '@mantine/core';
44

55
import { RowSidePanelContext } from './DBRowSidePanel';
66
import EventTag from './EventTag';
77

8+
const DEFAULT_ATTRIBUTES_TO_SHOW = 12;
9+
810
export type HighlightedAttribute = {
911
source: TSource;
1012
displayedKey: string;
@@ -18,22 +20,28 @@ export function DBHighlightedAttributesList({
1820
}: {
1921
attributes: HighlightedAttribute[];
2022
}) {
23+
const [isExpanded, setIsExpanded] = useState(false);
24+
2125
const {
2226
onPropertyAddClick,
2327
generateSearchUrl,
2428
source: contextSource,
2529
} = useContext(RowSidePanelContext);
2630

2731
const sortedAttributes = useMemo(() => {
28-
return attributes.sort(
29-
(a, b) =>
30-
a.displayedKey.localeCompare(b.displayedKey) ||
31-
a.value.localeCompare(b.value),
32-
);
33-
}, [attributes]);
32+
return attributes
33+
.sort(
34+
(a, b) =>
35+
a.displayedKey.localeCompare(b.displayedKey) ||
36+
a.value.localeCompare(b.value),
37+
)
38+
.slice(0, isExpanded ? attributes.length : DEFAULT_ATTRIBUTES_TO_SHOW);
39+
}, [attributes, isExpanded]);
40+
41+
const hiddenAttributesCount = attributes.length - sortedAttributes.length;
3442

3543
return (
36-
<Flex wrap="wrap" gap="2px" mb="md">
44+
<Flex wrap="wrap" gap="2px" mb="md" align="baseline">
3745
{sortedAttributes.map(({ displayedKey, value, sql, lucene, source }) => (
3846
<EventTag
3947
displayedKey={displayedKey}
@@ -62,6 +70,11 @@ export function DBHighlightedAttributesList({
6270
}
6371
/>
6472
))}
73+
{attributes.length > DEFAULT_ATTRIBUTES_TO_SHOW && (
74+
<Anchor size="xs" onClick={() => setIsExpanded(!isExpanded)}>
75+
{isExpanded ? 'Show Less' : `Show ${hiddenAttributesCount} More...`}
76+
</Anchor>
77+
)}
6578
</Flex>
6679
);
6780
}

packages/app/src/components/DBTraceWaterfallChart.tsx

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
SourceKind,
1111
TSource,
1212
} from '@hyperdx/common-utils/dist/types';
13-
import { Anchor, Box, Divider, Group, Text } from '@mantine/core';
13+
import { Anchor, Box, Code, Divider, Group, Text } from '@mantine/core';
1414

1515
import { ContactSupportText } from '@/components/ContactSupportText';
1616
import useOffsetPaginatedQuery from '@/hooks/useOffsetPaginatedQuery';
@@ -245,28 +245,37 @@ export function useEventsAroundFocus({
245245
/** A lucene expression that identifies rows to be hidden. Hidden rows will be returned with a `__hdx_hidden: true` column. */
246246
hiddenRowExpression?: string;
247247
}) {
248-
let isFetching = false;
249248
const { config, alias, type } = useMemo(
250249
() => getConfig(tableSource, traceId, hiddenRowExpression),
251250
[tableSource, traceId, hiddenRowExpression],
252251
);
253252

254-
const { data: beforeSpanData, isFetching: isBeforeSpanFetching } =
255-
useEventsData({
256-
config,
257-
dateRangeStartInclusive: true,
258-
dateRange: [dateRange[0], focusDate],
259-
enabled,
260-
});
261-
const { data: afterSpanData, isFetching: isAfterSpanFetching } =
262-
useEventsData({
263-
config,
264-
dateRangeStartInclusive: false,
265-
dateRange: [focusDate, dateRange[1]],
266-
enabled,
267-
});
268-
isFetching = isFetching || isBeforeSpanFetching || isAfterSpanFetching;
253+
const {
254+
data: beforeSpanData,
255+
isFetching: isBeforeSpanFetching,
256+
error: beforeSpanError,
257+
} = useEventsData({
258+
config,
259+
dateRangeStartInclusive: true,
260+
dateRange: [dateRange[0], focusDate],
261+
enabled,
262+
});
263+
264+
const {
265+
data: afterSpanData,
266+
isFetching: isAfterSpanFetching,
267+
error: afterSpanError,
268+
} = useEventsData({
269+
config,
270+
dateRangeStartInclusive: false,
271+
dateRange: [focusDate, dateRange[1]],
272+
enabled,
273+
});
274+
275+
const isFetching = isBeforeSpanFetching || isAfterSpanFetching;
269276
const meta = beforeSpanData?.meta ?? afterSpanData?.meta;
277+
const error = beforeSpanError || afterSpanError;
278+
270279
const rowWhere = useRowWhere({ meta, aliasMap: alias });
271280
const rows = useMemo(() => {
272281
// Sometimes meta has not loaded yet
@@ -294,6 +303,7 @@ export function useEventsAroundFocus({
294303
rows,
295304
meta,
296305
isFetching,
306+
error,
297307
};
298308
}
299309

@@ -352,6 +362,7 @@ export function DBTraceWaterfallChartContainer({
352362
rows: traceRowsData,
353363
isFetching: traceIsFetching,
354364
meta: traceRowsMeta,
365+
error: traceError,
355366
} = useEventsAroundFocus({
356367
tableSource: traceTableSource,
357368
focusDate,
@@ -364,6 +375,7 @@ export function DBTraceWaterfallChartContainer({
364375
rows: logRowsData,
365376
isFetching: logIsFetching,
366377
meta: logRowsMeta,
378+
error: logError,
367379
} = useEventsAroundFocus({
368380
// search data if logTableModel exist
369381
// search invalid date range if no logTableModel(react hook need execute no matter what)
@@ -376,6 +388,8 @@ export function DBTraceWaterfallChartContainer({
376388
});
377389

378390
const isFetching = traceIsFetching || logIsFetching;
391+
const error = traceError || logError;
392+
379393
const rows: any[] = useMemo(
380394
() => [...traceRowsData, ...logRowsData],
381395
[traceRowsData, logRowsData],
@@ -799,6 +813,9 @@ export function DBTraceWaterfallChartContainer({
799813
)}
800814
</span>
801815
</Group>
816+
{!isFetching && !error && (
817+
<DBHighlightedAttributesList attributes={highlightedAttributeValues} />
818+
)}
802819
<div
803820
style={{
804821
position: 'relative',
@@ -808,6 +825,20 @@ export function DBTraceWaterfallChartContainer({
808825
>
809826
{isFetching ? (
810827
<div className="my-3">Loading Traces...</div>
828+
) : error ? (
829+
<Box mt="lg">
830+
<Text my="sm" size="sm">
831+
An error occurred while fetching trace data:
832+
</Text>
833+
<Code
834+
block
835+
style={{
836+
whiteSpace: 'pre-wrap',
837+
}}
838+
>
839+
{error.message}
840+
</Code>
841+
</Box>
811842
) : rows == null ? (
812843
<div>
813844
An unknown error occurred. <ContactSupportText />
@@ -816,9 +847,6 @@ export function DBTraceWaterfallChartContainer({
816847
<div className="my-3">No matching spans or logs found</div>
817848
) : (
818849
<>
819-
<DBHighlightedAttributesList
820-
attributes={highlightedAttributeValues}
821-
/>
822850
<TimelineChart
823851
style={{
824852
overflowY: 'auto',

packages/app/src/components/EventTag.tsx

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useState } from 'react';
22
import Link from 'next/link';
33
import SqlString from 'sqlstring';
44
import { SearchConditionLanguage } from '@hyperdx/common-utils/dist/types';
5-
import { Button, Popover, Stack, Tooltip } from '@mantine/core';
5+
import { Button, Group, Popover, Stack, Text, Tooltip } from '@mantine/core';
66
import { IconLink } from '@tabler/icons-react';
77

88
import { isLinkableUrl } from '@/utils/highlightedAttributes';
@@ -62,14 +62,15 @@ export default function EventTag({
6262
onChange={setOpened}
6363
>
6464
<Popover.Target>
65-
{isLink ? (
66-
<Tooltip
67-
label={value}
68-
withArrow
69-
maw={400}
70-
multiline
71-
style={{ wordBreak: 'break-word' }}
72-
>
65+
<Tooltip
66+
label={value}
67+
withArrow
68+
maw={400}
69+
multiline
70+
style={{ wordBreak: 'break-word' }}
71+
position="bottom"
72+
>
73+
{isLink ? (
7374
<a
7475
href={encodeURI(value)}
7576
target="_blank"
@@ -79,15 +80,20 @@ export default function EventTag({
7980
{displayedKey || name}
8081
<IconLink size={14} className="ms-1" />
8182
</a>
82-
</Tooltip>
83-
) : (
84-
<div
85-
className="bg-highlighted px-2 py-0.5 me-1 my-1 cursor-pointer"
86-
onClick={() => setOpened(!opened)}
87-
>
88-
{displayedKey || name}: {value}
89-
</div>
90-
)}
83+
) : (
84+
<Group
85+
className="bg-highlighted px-2 py-0.5 me-1 my-1 cursor-pointer"
86+
wrap="nowrap"
87+
gap={0}
88+
onClick={() => setOpened(!opened)}
89+
>
90+
{displayedKey || name}:
91+
<Text size="xs" ms={4} maw={300} truncate>
92+
{value}
93+
</Text>
94+
</Group>
95+
)}
96+
</Tooltip>
9197
</Popover.Target>
9298
<Popover.Dropdown p={2}>
9399
<Stack gap={0} justify="stretch">

0 commit comments

Comments
 (0)