mirror of
https://github.com/SigNoz/signoz.git
synced 2026-11-06 08:20:42 +00:00
Compare commits
1 Commits
feat/formu
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c14d256e6f |
@@ -1,3 +1,9 @@
|
||||
import { logEventMock } from '__tests__/logEventMock';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import {
|
||||
OpenTraceSource,
|
||||
TracesExplorerEvents,
|
||||
} from 'container/TracesExplorer/events';
|
||||
import { getCurrentLocation } from 'lib/router/navigation';
|
||||
import { server } from 'mocks-server/server';
|
||||
import { screen, userEvent, waitFor, within } from 'tests/test-utils';
|
||||
@@ -44,6 +50,12 @@ jest.mock('container/OptionsMenu/useOptionsMenu', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
const ROW_TEST_ID = `traces-table-row-${listRows[0].data.span_id}`;
|
||||
|
||||
// The row click handler sits on each cell, so a click on the <tr> does nothing.
|
||||
const getRowCell = (row: HTMLElement): HTMLElement =>
|
||||
within(row).getAllByRole('cell')[0];
|
||||
|
||||
describe('Traces ListView - span drawer navigation', () => {
|
||||
beforeEach(() => {
|
||||
mockSuccess();
|
||||
@@ -134,6 +146,63 @@ describe('Traces ListView - span drawer navigation', () => {
|
||||
);
|
||||
expect(getCurrentLocation().pathname).toBe('/traces-explorer');
|
||||
expect(screen.queryByTestId('span-details-drawer')).not.toBeInTheDocument();
|
||||
expect(logEventMock).toHaveBeenCalledWith(
|
||||
TracesExplorerEvents.OpenTraceClicked,
|
||||
{
|
||||
panelType: PANEL_TYPES.LIST,
|
||||
source: OpenTraceSource.RowIcon,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('opens the trace page in a new tab from the drawer', async () => {
|
||||
const open = jest.spyOn(window, 'open').mockImplementation(() => null);
|
||||
const user = userEvent.setup();
|
||||
renderListView();
|
||||
|
||||
await user.click(getRowCell(await screen.findByTestId(ROW_TEST_ID)));
|
||||
expect(logEventMock).not.toHaveBeenCalledWith(
|
||||
TracesExplorerEvents.OpenTraceClicked,
|
||||
{ panelType: PANEL_TYPES.LIST, source: OpenTraceSource.Row },
|
||||
);
|
||||
await user.click(await screen.findByTestId('span-drawer-view-full-trace'));
|
||||
|
||||
expect(open).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/\/trace\/0000000000000000344ded1387b08a7e\?/),
|
||||
'_blank',
|
||||
);
|
||||
expect(logEventMock).toHaveBeenCalledWith(
|
||||
TracesExplorerEvents.OpenTraceClicked,
|
||||
{
|
||||
panelType: PANEL_TYPES.LIST,
|
||||
source: OpenTraceSource.SpanDrawer,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('opens the trace page in a new tab on a Cmd-click without opening the drawer', async () => {
|
||||
const open = jest.spyOn(window, 'open').mockImplementation(() => null);
|
||||
const user = userEvent.setup();
|
||||
renderListView();
|
||||
|
||||
const cell = getRowCell(await screen.findByTestId(ROW_TEST_ID));
|
||||
await user.keyboard('{Meta>}');
|
||||
await user.click(cell);
|
||||
await user.keyboard('{/Meta}');
|
||||
|
||||
expect(open).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/\/trace\/0000000000000000344ded1387b08a7e\?/),
|
||||
'_blank',
|
||||
'noopener',
|
||||
);
|
||||
expect(screen.queryByTestId('span-details-drawer')).not.toBeInTheDocument();
|
||||
expect(logEventMock).toHaveBeenCalledWith(
|
||||
TracesExplorerEvents.OpenTraceClicked,
|
||||
{
|
||||
panelType: PANEL_TYPES.LIST,
|
||||
source: OpenTraceSource.Row,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('shows the row icon only on the hovered copy of a duplicate span', async () => {
|
||||
|
||||
@@ -146,7 +146,9 @@ function ListView({
|
||||
(field) => field.name !== TIMESTAMP_FIELD.name,
|
||||
),
|
||||
];
|
||||
const fieldColumns = fields.map((field) => getFieldColumn(field));
|
||||
const fieldColumns = fields.map((field) =>
|
||||
getFieldColumn(field, PANEL_TYPES.LIST),
|
||||
);
|
||||
return [openTraceColumn, ...fieldColumns];
|
||||
}, [options?.selectColumns, openTraceColumn]);
|
||||
|
||||
|
||||
@@ -2,7 +2,12 @@ import { MouseEvent } from 'react';
|
||||
import { CornerUpRight } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { useIsRowHovered } from 'components/TanStackTableView/TanStackTableStateContext';
|
||||
import {
|
||||
logOpenTraceClicked,
|
||||
OpenTraceSource,
|
||||
} from 'container/TracesExplorer/events';
|
||||
import { getTraceLink } from 'container/TracesExplorer/ListView/utils';
|
||||
import { TracesTableRow } from 'container/TracesExplorer/TracesTable/getFieldColumn';
|
||||
import { openInNewTab } from 'utils/navigation';
|
||||
@@ -23,6 +28,7 @@ function OpenTraceButton({
|
||||
const handleClick = (event: MouseEvent): void => {
|
||||
// The row click would open the drawer instead.
|
||||
event.stopPropagation();
|
||||
logOpenTraceClicked(PANEL_TYPES.LIST, OpenTraceSource.RowIcon);
|
||||
openInNewTab(getTraceLink(row));
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,12 @@ import { ButtonGroup } from '@signozhq/ui/button-group';
|
||||
import { ChevronDown, ChevronUp, CornerUpRight } from '@signozhq/icons';
|
||||
import { useGetTraceSummary } from 'api/generated/services/tracedetail';
|
||||
import { HeaderAction } from 'components/DetailsPanel';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import type { UseRowNavigationResult } from 'components/TanStackTableView/useRowNavigation';
|
||||
import {
|
||||
logOpenTraceClicked,
|
||||
OpenTraceSource,
|
||||
} from 'container/TracesExplorer/events';
|
||||
import { getTraceLink } from 'container/TracesExplorer/ListView/utils';
|
||||
import { TracesTableRow } from 'container/TracesExplorer/TracesTable/getFieldColumn';
|
||||
import { SpanDetailVariant } from 'pages/TraceDetailsV3/SpanDetailsPanel/constants';
|
||||
@@ -48,7 +53,10 @@ function SpanDrawer({
|
||||
color="secondary"
|
||||
size="md"
|
||||
prefix={<CornerUpRight />}
|
||||
onClick={(): void => openInNewTab(getTraceLink(row))}
|
||||
onClick={(): void => {
|
||||
logOpenTraceClicked(PANEL_TYPES.LIST, OpenTraceSource.SpanDrawer);
|
||||
openInNewTab(getTraceLink(row));
|
||||
}}
|
||||
testId="span-drawer-view-full-trace"
|
||||
>
|
||||
View full trace
|
||||
|
||||
@@ -4,7 +4,12 @@ import { Minus } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import TanStackTable from 'components/TanStackTableView';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import {
|
||||
logOpenTraceClicked,
|
||||
OpenTraceSource,
|
||||
} from 'container/TracesExplorer/events';
|
||||
import { getMs } from 'utils/timeUtils';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
|
||||
@@ -21,9 +26,10 @@ import styles from './FieldCell.module.scss';
|
||||
type FieldCellProps = {
|
||||
name: string;
|
||||
value: unknown;
|
||||
panelType: PANEL_TYPES;
|
||||
};
|
||||
|
||||
function FieldCell({ name, value }: FieldCellProps): JSX.Element {
|
||||
function FieldCell({ name, value, panelType }: FieldCellProps): JSX.Element {
|
||||
const { formatTimezoneAdjustedTimestamp } = useTimezone();
|
||||
|
||||
if (TIMESTAMP_FIELD_NAMES.has(name)) {
|
||||
@@ -54,7 +60,15 @@ function FieldCell({ name, value }: FieldCellProps): JSX.Element {
|
||||
<AppLink
|
||||
to={buildRoutePath(ROUTES.TRACE_DETAIL, { id: text })}
|
||||
data-testid="trace-id"
|
||||
onClick={(e): void => e.stopPropagation()}
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
logOpenTraceClicked(panelType, OpenTraceSource.TraceId);
|
||||
}}
|
||||
onAuxClick={(e): void => {
|
||||
if (e.button === 1) {
|
||||
logOpenTraceClicked(panelType, OpenTraceSource.TraceId);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</AppLink>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useRef } from 'react';
|
||||
import { useAppNavigate } from 'lib/router/useAppNavigate';
|
||||
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
|
||||
import TanStackTable from 'components/TanStackTableView';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import type {
|
||||
CellTypographySize,
|
||||
TableColumnDef,
|
||||
@@ -10,6 +11,10 @@ import { useRecoverFromEmptyPage } from 'components/TanStackTableView/useRecover
|
||||
import EmptyLogsSearch from 'container/EmptyLogsSearch/EmptyLogsSearch';
|
||||
import NoLogs from 'container/NoLogs/NoLogs';
|
||||
import { TracesLoading } from 'container/TracesExplorer/TraceLoading/TraceLoading';
|
||||
import {
|
||||
logOpenTraceClicked,
|
||||
OpenTraceSource,
|
||||
} from 'container/TracesExplorer/events';
|
||||
import APIError from 'types/api/error';
|
||||
import { DataSource, PanelTypeKeys } from 'types/common/queryBuilder';
|
||||
import { noop } from 'lodash-es';
|
||||
@@ -101,16 +106,18 @@ function TracesTable({
|
||||
onRowClick(row);
|
||||
return;
|
||||
}
|
||||
logOpenTraceClicked(PANEL_TYPES[panelType], OpenTraceSource.Row);
|
||||
navigate(getRowHref(row));
|
||||
},
|
||||
[onRowClick, navigate, getRowHref],
|
||||
[onRowClick, navigate, getRowHref, panelType],
|
||||
);
|
||||
|
||||
const handleRowClickNewTab = useCallback(
|
||||
(row: TracesTableRow): void => {
|
||||
logOpenTraceClicked(PANEL_TYPES[panelType], OpenTraceSource.Row);
|
||||
window.open(getAbsoluteUrl(getRowHref(row)), '_blank', 'noopener');
|
||||
},
|
||||
[getRowHref],
|
||||
[getRowHref, panelType],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,6 +2,7 @@ import { TelemetryFieldKey } from 'api/v5/v5';
|
||||
import type { TableColumnDef } from 'components/TanStackTableView/types';
|
||||
import { buildCompositeKey } from 'container/OptionsMenu/utils';
|
||||
import { getSpanAttribute } from 'pages/TraceDetailsV3/utils';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
|
||||
import { TIMESTAMP_FIELD_NAMES } from './constants';
|
||||
import FieldCell from './FieldCell';
|
||||
@@ -10,6 +11,7 @@ export type TracesTableRow = { id: string } & Record<string, unknown>;
|
||||
|
||||
export function getFieldColumn(
|
||||
field: TelemetryFieldKey,
|
||||
panelType: PANEL_TYPES,
|
||||
): TableColumnDef<TracesTableRow> {
|
||||
const { name, fieldContext, fieldDataType } = field;
|
||||
const isTimestamp = TIMESTAMP_FIELD_NAMES.has(name);
|
||||
@@ -25,6 +27,8 @@ export function getFieldColumn(
|
||||
enableRemove: !isTimestamp,
|
||||
canBeHidden: !isTimestamp,
|
||||
width: { min: 192 },
|
||||
cell: ({ value }): JSX.Element => <FieldCell name={name} value={value} />,
|
||||
cell: ({ value }): JSX.Element => (
|
||||
<FieldCell name={name} value={value} panelType={panelType} />
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { logEventMock } from '__tests__/logEventMock';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import {
|
||||
OpenTraceSource,
|
||||
TracesExplorerEvents,
|
||||
} from 'container/TracesExplorer/events';
|
||||
import { getCurrentLocation } from 'lib/router/navigation';
|
||||
import { server } from 'mocks-server/server';
|
||||
import { rest } from 'msw';
|
||||
import { screen, userEvent, waitFor, within } from 'tests/test-utils';
|
||||
|
||||
import {
|
||||
groupedResponse,
|
||||
groupedRows,
|
||||
QUERY_RANGE_URL,
|
||||
renderTracesView,
|
||||
} from './testUtils';
|
||||
|
||||
const TRACE_ID = '0000000000000000344ded1387b08a7e';
|
||||
const ROW_TEST_ID = `traces-table-row-${TRACE_ID}`;
|
||||
|
||||
describe('TracesView - open trace clicked', () => {
|
||||
beforeEach(() => {
|
||||
server.use(
|
||||
rest.post(QUERY_RANGE_URL, (_req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(groupedResponse(groupedRows))),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
server.resetHandlers();
|
||||
});
|
||||
|
||||
it('logs a row click that opens the trace page', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderTracesView();
|
||||
|
||||
const row = await screen.findByTestId(ROW_TEST_ID);
|
||||
// The row click handler sits on each cell, so a click on the <tr> does nothing.
|
||||
await user.click(within(row).getAllByRole('cell')[0]);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(getCurrentLocation().pathname).toBe(`/trace/${TRACE_ID}`),
|
||||
);
|
||||
expect(logEventMock).toHaveBeenCalledWith(
|
||||
TracesExplorerEvents.OpenTraceClicked,
|
||||
{
|
||||
panelType: PANEL_TYPES.TRACE,
|
||||
source: OpenTraceSource.Row,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('logs a trace_id link click', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderTracesView();
|
||||
|
||||
const row = await screen.findByTestId(ROW_TEST_ID);
|
||||
await user.click(within(row).getByTestId('trace-id'));
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledWith(
|
||||
TracesExplorerEvents.OpenTraceClicked,
|
||||
{
|
||||
panelType: PANEL_TYPES.TRACE,
|
||||
source: OpenTraceSource.TraceId,
|
||||
},
|
||||
);
|
||||
expect(logEventMock).not.toHaveBeenCalledWith(
|
||||
TracesExplorerEvents.OpenTraceClicked,
|
||||
{ panelType: PANEL_TYPES.TRACE, source: OpenTraceSource.Row },
|
||||
);
|
||||
});
|
||||
|
||||
it('logs a middle click on the trace_id link', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderTracesView();
|
||||
|
||||
const row = await screen.findByTestId(ROW_TEST_ID);
|
||||
await user.pointer({
|
||||
keys: '[MouseMiddle]',
|
||||
target: within(row).getByTestId('trace-id'),
|
||||
});
|
||||
|
||||
expect(logEventMock).toHaveBeenCalledWith(
|
||||
TracesExplorerEvents.OpenTraceClicked,
|
||||
{
|
||||
panelType: PANEL_TYPES.TRACE,
|
||||
source: OpenTraceSource.TraceId,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { TelemetryFieldKey } from 'api/v5/v5';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import type { TableColumnDef } from 'components/TanStackTableView/types';
|
||||
import {
|
||||
getFieldColumn,
|
||||
@@ -15,7 +16,7 @@ const TRACE_FIELDS = [
|
||||
|
||||
export const columns: TableColumnDef<TracesTableRow>[] = TRACE_FIELDS.map(
|
||||
(field) => ({
|
||||
...getFieldColumn(field),
|
||||
...getFieldColumn(field, PANEL_TYPES.TRACE),
|
||||
enableRemove: false,
|
||||
canBeHidden: false,
|
||||
}),
|
||||
|
||||
20
frontend/src/container/TracesExplorer/events.ts
Normal file
20
frontend/src/container/TracesExplorer/events.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
|
||||
export enum TracesExplorerEvents {
|
||||
OpenTraceClicked = 'Traces Explorer: Open trace clicked',
|
||||
}
|
||||
|
||||
export enum OpenTraceSource {
|
||||
Row = 'row',
|
||||
RowIcon = 'row_icon',
|
||||
TraceId = 'trace_id',
|
||||
SpanDrawer = 'span_drawer',
|
||||
}
|
||||
|
||||
export function logOpenTraceClicked(
|
||||
panelType: PANEL_TYPES,
|
||||
source: OpenTraceSource,
|
||||
): void {
|
||||
void logEvent(TracesExplorerEvents.OpenTraceClicked, { panelType, source });
|
||||
}
|
||||
Reference in New Issue
Block a user