mirror of
https://github.com/SigNoz/signoz.git
synced 2026-08-02 03:10:33 +01:00
* fix(logs): open log details in table (Column) format when activeId is set
Opening a log link (?activeLogId=...) and switching to Column/table format
left the highlighted row un-openable — clicking it did nothing.
Root cause: the shared TanStackTable decided open vs. close internally from
`isRowActive` + `onRowDeactivate`. LogsExplorerList had overloaded `isRowActive`
with `|| log.id === activeLogId`, so a URL-highlighted-but-closed row looked
"active" and the first click ran the deactivate (close) path instead of opening.
Fix (PR 1 of 2): move click routing to the consumer.
- Remove `onRowDeactivate` from TanStackTable; it took no args and only fired
when re-clicking the same active row (clicking A→B never deactivated A).
- `onRowClick` now receives a third `{ isActive }` context arg (additive; the
~14 existing consumers are unaffected).
- LogsExplorerList and LiveLogsList own the toggle:
`isActive ? handleCloseLogDetail() : handleSetActiveLog(log)`, and
`isRowActive` tracks only `activeLog?.id`.
The linked-row visual highlight (`isHighlighted`) is deferred to PR 2 (blocked
on a design decision for the highlight color); see
frontend/docs/tdd-tanstack-row-click-routing.md.
* feat(logs): highlight the URL-linked (activeLogId) row in table format (#12271)
Restores the linked-row highlight that the TanStack migration (#10946)
dropped from the table view, without coupling it to click routing.
Uses the table's existing `getRowClassName` hook (styling-only) — no new
shared-table prop:
- LogsExplorerList / LiveLogsList set getRowClassName to 'logs-linked-row'
when `log.id === activeLogId`.
- A global, lowest-specificity rule paints `.logs-linked-row td` using the
per-row `--row-active-bg` var (already set via getRowStyle). The table's own
:hover / .tableRowActive cell rules are higher-specificity !important, so
active/hover correctly win when a linked row is also open or hovered.
`isRowActive` stays `activeLog?.id` only, so the highlighted row still opens on
first click (relies on the routing fix in the base PR).
Color reuses --row-active-bg as a placeholder; a distinct "linked" token is a
follow-up pending design.
Co-authored-by: Gaurav Tewari <tewarig@users.noreply.github.com>
* chore: remove comments
* chore: remove extra comments
* feat: add e2e for flows
* chore: add test id
* chore: chore: remove comment
* chore: remove comment
* chore: remove comment
* chore: add comment
* chore: remove comment
---------
Co-authored-by: Gaurav Tewari <tewarig@users.noreply.github.com>
180 lines
4.9 KiB
TypeScript
180 lines
4.9 KiB
TypeScript
import { ComponentProps, memo, useCallback, useLayoutEffect } from 'react';
|
|
import { TableComponents } from 'react-virtuoso';
|
|
import cx from 'classnames';
|
|
|
|
import {
|
|
chromePerformanceTanstackTableBeginHover,
|
|
chromePerformanceMeasureTanstackTable,
|
|
} from './perfDevtools';
|
|
import TanStackRowCells from './TanStackRow';
|
|
import {
|
|
useClearRowHovered,
|
|
useSetRowHovered,
|
|
} from './TanStackTableStateContext';
|
|
import { FlatItem, TableRowContext } from './types';
|
|
|
|
import tableStyles from './TanStackTable.module.scss';
|
|
import { chromePerformanceNow } from 'lib/chromePerformanceDevTools';
|
|
|
|
type VirtuosoTableRowProps<TData, TItemKey = string> = ComponentProps<
|
|
NonNullable<
|
|
TableComponents<FlatItem<TData>, TableRowContext<TData, TItemKey>>['TableRow']
|
|
>
|
|
>;
|
|
|
|
function TanStackCustomTableRow<TData, TItemKey = string>({
|
|
item,
|
|
context,
|
|
...props
|
|
}: VirtuosoTableRowProps<TData, TItemKey>): JSX.Element {
|
|
const renderStart = chromePerformanceNow();
|
|
const rowId = item.row.id;
|
|
const rowData = item.row.original;
|
|
|
|
// Stable callbacks for hover state management
|
|
const setHovered = useSetRowHovered(rowId);
|
|
const clearHovered = useClearRowHovered(rowId);
|
|
|
|
const handleMouseEnter = useCallback((): void => {
|
|
chromePerformanceTanstackTableBeginHover(rowId);
|
|
setHovered();
|
|
}, [rowId, setHovered]);
|
|
|
|
useLayoutEffect(() => {
|
|
chromePerformanceMeasureTanstackTable('Row render', renderStart, {
|
|
track: 'Row render',
|
|
color: 'secondary',
|
|
tooltipText: 'Row render + commit',
|
|
properties: [
|
|
['rowId', rowId],
|
|
['kind', item.kind],
|
|
],
|
|
});
|
|
});
|
|
|
|
if (item.kind === 'expansion') {
|
|
return (
|
|
<tr {...props} className={tableStyles.tableRowExpansion}>
|
|
<TanStackRowCells
|
|
row={item.row}
|
|
itemKind={item.kind}
|
|
context={context}
|
|
hasSingleColumn={context?.hasSingleColumn ?? false}
|
|
columnOrderKey={context?.columnOrderKey ?? ''}
|
|
columnVisibilityKey={context?.columnVisibilityKey ?? ''}
|
|
/>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
const isActive = context?.isRowActive?.(rowData) ?? false;
|
|
const extraClass = context?.getRowClassName?.(rowData) ?? '';
|
|
const rowStyle = context?.getRowStyle?.(rowData);
|
|
const enableAlternatingRowColors =
|
|
context?.enableAlternatingRowColors ?? false;
|
|
|
|
const rowClassName = cx(
|
|
tableStyles.tableRow,
|
|
isActive && tableStyles.tableRowActive,
|
|
enableAlternatingRowColors &&
|
|
(item.row.index % 2 === 0
|
|
? tableStyles.tableRowEven
|
|
: tableStyles.tableRowOdd),
|
|
extraClass,
|
|
);
|
|
|
|
return (
|
|
<tr
|
|
{...props}
|
|
className={rowClassName}
|
|
style={rowStyle}
|
|
data-testid={context?.getRowTestId?.(rowData)}
|
|
onMouseEnter={handleMouseEnter}
|
|
onMouseLeave={clearHovered}
|
|
>
|
|
<TanStackRowCells
|
|
row={item.row}
|
|
itemKind={item.kind}
|
|
context={context}
|
|
hasSingleColumn={context?.hasSingleColumn ?? false}
|
|
columnOrderKey={context?.columnOrderKey ?? ''}
|
|
columnVisibilityKey={context?.columnVisibilityKey ?? ''}
|
|
/>
|
|
</tr>
|
|
);
|
|
}
|
|
|
|
// Custom comparison - only re-render when row identity or computed values change
|
|
// This looks overkill but ensures the table is stable and doesn't re-render on every change
|
|
// If you add any new prop to context, remember to update this function
|
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
function areTableRowPropsEqual<TData, TItemKey = string>(
|
|
prev: Readonly<VirtuosoTableRowProps<TData, TItemKey>>,
|
|
next: Readonly<VirtuosoTableRowProps<TData, TItemKey>>,
|
|
): boolean {
|
|
if (prev.item.row.id !== next.item.row.id) {
|
|
return false;
|
|
}
|
|
if (prev.item.kind !== next.item.kind) {
|
|
return false;
|
|
}
|
|
|
|
const prevData = prev.item.row.original;
|
|
const nextData = next.item.row.original;
|
|
|
|
if (prevData !== nextData) {
|
|
return false;
|
|
}
|
|
|
|
if (prev.context?.hasSingleColumn !== next.context?.hasSingleColumn) {
|
|
return false;
|
|
}
|
|
if (prev.context?.columnOrderKey !== next.context?.columnOrderKey) {
|
|
return false;
|
|
}
|
|
if (prev.context?.columnVisibilityKey !== next.context?.columnVisibilityKey) {
|
|
return false;
|
|
}
|
|
if (
|
|
prev.context?.enableAlternatingRowColors !==
|
|
next.context?.enableAlternatingRowColors
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
if (prev.context !== next.context) {
|
|
const prevActive = prev.context?.isRowActive?.(prevData) ?? false;
|
|
const nextActive = next.context?.isRowActive?.(nextData) ?? false;
|
|
if (prevActive !== nextActive) {
|
|
return false;
|
|
}
|
|
|
|
const prevClass = prev.context?.getRowClassName?.(prevData) ?? '';
|
|
const nextClass = next.context?.getRowClassName?.(nextData) ?? '';
|
|
if (prevClass !== nextClass) {
|
|
return false;
|
|
}
|
|
|
|
const prevTestId = prev.context?.getRowTestId?.(prevData) ?? '';
|
|
const nextTestId = next.context?.getRowTestId?.(nextData) ?? '';
|
|
if (prevTestId !== nextTestId) {
|
|
return false;
|
|
}
|
|
|
|
const prevStyle = prev.context?.getRowStyle?.(prevData);
|
|
const nextStyle = next.context?.getRowStyle?.(nextData);
|
|
if (prevStyle !== nextStyle) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
export default memo(TanStackCustomTableRow, areTableRowPropsEqual as any) as <
|
|
TData,
|
|
TItemKey = string,
|
|
>(
|
|
props: VirtuosoTableRowProps<TData, TItemKey>,
|
|
) => JSX.Element;
|