Skip to content

Commit 970c002

Browse files
authored
Improve Discoverability Inline Expansion of Items in Search Table (#1168)
1 parent 3d82583 commit 970c002

File tree

4 files changed

+207
-56
lines changed

4 files changed

+207
-56
lines changed

.changeset/purple-pens-grab.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": minor
3+
---
4+
5+
Fix: improve the discoverability of inline item expansion within the search table

packages/app/src/components/DBRowTable.tsx

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,7 @@ import {
4343
Tooltip as MantineTooltip,
4444
UnstyledButton,
4545
} from '@mantine/core';
46-
import {
47-
FetchNextPageOptions,
48-
useQuery,
49-
useQueryClient,
50-
} from '@tanstack/react-query';
46+
import { FetchNextPageOptions, useQuery } from '@tanstack/react-query';
5147
import {
5248
ColumnDef,
5349
ColumnResizeMode,
@@ -696,11 +692,7 @@ export const RawLogTable = memo(
696692
config={config}
697693
/>
698694
)}
699-
<table
700-
className="w-100 bg-inherit"
701-
id={tableId}
702-
style={{ tableLayout: 'fixed' }}
703-
>
695+
<table className={cx('w-100 bg-inherit', styles.table)} id={tableId}>
704696
<thead className={styles.tableHead}>
705697
{table.getHeaderGroups().map(headerGroup => (
706698
<tr key={headerGroup.id}>
@@ -832,34 +824,82 @@ export const RawLogTable = memo(
832824
<React.Fragment key={virtualRow.key}>
833825
<tr
834826
data-testid={`table-row-${rowId}`}
835-
onClick={() => {
836-
// onRowExpandClick(row.original.id, row.original.sort_key);
837-
_onRowExpandClick(row.original);
838-
}}
839-
role="button"
840-
// TODO: Restore highlight
841827
className={cx(styles.tableRow, {
842828
[styles.tableRow__selected]: highlightedLineId === rowId,
843829
})}
844830
data-index={virtualRow.index}
845831
ref={rowVirtualizer.measureElement}
846832
>
847-
{row.getVisibleCells().map(cell => {
848-
return (
849-
<td
850-
key={cell.id}
851-
className={cx('align-top overflow-hidden', {
852-
'text-break': wrapLinesEnabled,
853-
'text-truncate': !wrapLinesEnabled,
833+
{/* Expand button cell */}
834+
{showExpandButton && (
835+
<td
836+
className="align-top overflow-hidden"
837+
style={{ width: '40px' }}
838+
>
839+
{flexRender(
840+
row.getVisibleCells()[0].column.columnDef.cell,
841+
row.getVisibleCells()[0].getContext(),
842+
)}
843+
</td>
844+
)}
845+
846+
{/* Content columns grouped as one button */}
847+
<td
848+
className="align-top overflow-hidden p-0"
849+
colSpan={columns.length - (showExpandButton ? 1 : 0)}
850+
>
851+
<button
852+
type="button"
853+
className={styles.rowContentButton}
854+
onClick={e => {
855+
e.stopPropagation();
856+
_onRowExpandClick(row.original);
857+
}}
858+
aria-label="View details for log entry"
859+
>
860+
{row
861+
.getVisibleCells()
862+
.slice(showExpandButton ? 1 : 0) // Skip expand column
863+
.map((cell, cellIndex) => {
864+
const columnCustomClassName = (
865+
cell.column.columnDef.meta as any
866+
)?.className;
867+
const columnSize = cell.column.getSize();
868+
const totalContentCells =
869+
row.getVisibleCells().length -
870+
(showExpandButton ? 1 : 0);
871+
872+
return (
873+
<div
874+
key={cell.id}
875+
className={cx(
876+
'flex-shrink-0 overflow-hidden',
877+
{
878+
'text-break': wrapLinesEnabled,
879+
'text-truncate': !wrapLinesEnabled,
880+
},
881+
columnCustomClassName,
882+
)}
883+
style={{
884+
width:
885+
columnSize === UNDEFINED_WIDTH
886+
? 'auto'
887+
: `${columnSize}px`,
888+
flex:
889+
columnSize === UNDEFINED_WIDTH
890+
? '1'
891+
: 'none',
892+
}}
893+
>
894+
{flexRender(
895+
cell.column.columnDef.cell,
896+
cell.getContext(),
897+
)}
898+
</div>
899+
);
854900
})}
855-
>
856-
{flexRender(
857-
cell.column.columnDef.cell,
858-
cell.getContext(),
859-
)}
860-
</td>
861-
);
862-
})}
901+
</button>
902+
</td>
863903
</tr>
864904
{showExpandButton && isExpanded && (
865905
<ExpandedLogRow
@@ -1213,8 +1253,6 @@ function DBSqlRowTableComponent({
12131253
return noisyPatterns.data?.map(p => p.id) ?? [];
12141254
}, [noisyPatterns.data]);
12151255

1216-
const queryClient = useQueryClient();
1217-
12181256
const denoisedRows = useQuery({
12191257
queryKey: [
12201258
'denoised-rows',

packages/app/src/components/ExpandableRowTable.tsx

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { memo, useCallback, useState } from 'react';
22
import cx from 'classnames';
33
import { useQueryState } from 'nuqs';
44
import { TSource } from '@hyperdx/common-utils/dist/types';
5+
import { IconChevronRight } from '@tabler/icons-react';
56

67
import { useLocalStorage } from '@/utils';
78

@@ -179,6 +180,44 @@ export const useExpandableRows = (
179180
};
180181
};
181182

183+
const ExpandButton = memo(
184+
({
185+
rowId,
186+
isExpanded,
187+
highlightedLineId,
188+
toggleRowExpansion,
189+
}: {
190+
rowId: string;
191+
isExpanded: boolean;
192+
highlightedLineId?: string;
193+
toggleRowExpansion: (rowId: string) => void;
194+
}) => {
195+
return (
196+
<span className="d-flex align-items-center justify-content-center">
197+
<button
198+
type="button"
199+
className={cx(styles.expandButton, {
200+
[styles.expanded]: isExpanded,
201+
'text-success': highlightedLineId === rowId,
202+
'text-muted': highlightedLineId !== rowId,
203+
})}
204+
onClick={e => {
205+
e.stopPropagation();
206+
toggleRowExpansion(rowId);
207+
}}
208+
aria-expanded={isExpanded}
209+
aria-label={`${isExpanded ? 'Collapse' : 'Expand'} log details`}
210+
>
211+
<IconChevronRight size={16} />
212+
</button>
213+
<span className={styles.expandButtonSeparator} />
214+
</span>
215+
);
216+
},
217+
);
218+
219+
ExpandButton.displayName = 'ExpandButton';
220+
182221
// Utility function for creating expand button column
183222
export const createExpandButtonColumn = (
184223
expandedRows: Record<string, boolean>,
@@ -191,25 +230,19 @@ export const createExpandButtonColumn = (
191230
cell: (info: any) => {
192231
const rowId = info.getValue() as string;
193232
const isExpanded = expandedRows[rowId] ?? false;
233+
194234
return (
195-
<button
196-
type="button"
197-
className={cx('btn btn-link p-0 border-0', {
198-
'text-success': highlightedLineId === rowId,
199-
'text-muted': highlightedLineId !== rowId,
200-
})}
201-
onClick={e => {
202-
e.stopPropagation();
203-
toggleRowExpansion(rowId);
204-
}}
205-
aria-expanded={isExpanded}
206-
aria-label={`${isExpanded ? 'Collapse' : 'Expand'} log details`}
207-
style={{ lineHeight: 1 }}
208-
>
209-
<i className={`bi bi-chevron-${isExpanded ? 'down' : 'right'}`} />
210-
</button>
235+
<ExpandButton
236+
rowId={rowId}
237+
isExpanded={isExpanded}
238+
highlightedLineId={highlightedLineId}
239+
toggleRowExpansion={toggleRowExpansion}
240+
/>
211241
);
212242
},
213-
size: 8,
243+
size: 32,
214244
enableResizing: false,
245+
meta: {
246+
className: 'text-center',
247+
},
215248
});

packages/app/styles/LogTable.module.scss

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
@import './variables';
22

3+
.table {
4+
table-layout: fixed;
5+
border-spacing: 0 2px;
6+
border-collapse: separate;
7+
}
8+
39
.tableHead {
410
background: inherit;
511
position: sticky;
612
top: 0;
713
}
814

915
.tableRow {
10-
&:hover {
11-
background-color: $slate-800;
12-
}
13-
1416
&__selected {
1517
background-color: $slate-800;
1618
font-weight: bold;
@@ -25,8 +27,81 @@
2527
}
2628

2729
.expandButton {
30+
background: transparent;
31+
border: none;
32+
padding: 0 2px;
33+
color: inherit;
34+
cursor: pointer;
35+
display: inline-flex;
36+
align-items: center;
37+
justify-content: center;
38+
transition: background-color 0.15s ease-in-out;
39+
border-radius: 4px;
40+
41+
&:hover {
42+
background-color: $slate-800;
43+
}
44+
45+
&:focus,
46+
&:focus-visible {
47+
background-color: $slate-800;
48+
outline: none;
49+
}
50+
51+
&:active {
52+
outline: none;
53+
box-shadow: none;
54+
background-color: $slate-800;
55+
}
56+
57+
svg {
58+
transition: transform 0.2s ease-in-out;
59+
transform-origin: center;
60+
}
61+
62+
&.expanded svg {
63+
transform: rotate(90deg);
64+
}
65+
}
66+
67+
.expandButtonSeparator {
68+
width: 1px;
69+
height: 12px;
70+
border-right: 1px solid $slate-800;
71+
margin-left: 2px;
72+
margin-right: 2px;
73+
display: inline-block;
74+
vertical-align: middle;
75+
}
76+
77+
.rowContentButton {
78+
background: transparent;
79+
border: none;
80+
padding: 0;
81+
margin: 0;
82+
width: 100%;
83+
height: 100%;
84+
cursor: pointer;
85+
display: flex;
86+
align-items: stretch;
87+
text-align: left;
88+
color: inherit;
89+
transition: background-color 0.15s ease-in-out;
90+
border-radius: 4px;
91+
2892
&:hover {
29-
background-color: rgb(255 255 255 / 10%) !important;
30-
color: #fff !important;
93+
background-color: $slate-800;
94+
}
95+
96+
&:focus,
97+
&:focus-visible {
98+
background-color: $slate-800;
99+
outline: none;
100+
}
101+
102+
&:active {
103+
outline: none;
104+
box-shadow: none;
105+
background-color: $slate-800;
31106
}
32107
}

0 commit comments

Comments
 (0)