Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Traces - Custom source switch to data grid #2390

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
inject html to format euidatagrid
Signed-off-by: Adam Tackett <tackadam@amazon.com>
Adam Tackett committed Mar 20, 2025
commit 5e42e1130221391a9eb4c382d79f4083443bfad9
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
import { EuiEmptyPrompt, EuiSmallButtonEmpty, EuiSpacer, EuiText } from '@elastic/eui';
import { SpacerSize } from '@elastic/eui/src/components/spacer/spacer';
import { isEmpty, round } from 'lodash';
import React from 'react';
import React, { useEffect } from 'react';
import {
DATA_PREPPER_INDEX_NAME,
DATA_PREPPER_SERVICE_INDEX_NAME,
@@ -252,11 +252,11 @@

const averageLatency = `Average duration: ${
map[service].latency! >= 0 ? map[service].latency + 'ms' : '-'
}`;
}`;

Check failure on line 255 in public/components/trace_analytics/components/common/helper_functions.tsx

GitHub Actions / Lint

Delete `··`

const errorRate = `Error rate: ${
map[service].error_rate! >= 0 ? map[service].error_rate + '%' : '-'
}`;
}`;

Check failure on line 259 in public/components/trace_analytics/components/common/helper_functions.tsx

GitHub Actions / Lint

Delete `··`

const throughput =
'Request rate: ' +
@@ -360,7 +360,7 @@
percentileMaps: Array<{ traceGroupName: string; durationFilter: { gte?: number; lte?: number } }>,
conditionString: string // >= 95th, < 95th
): FilterType => {
const DSL: any = {

Check warning on line 363 in public/components/trace_analytics/components/common/helper_functions.tsx

GitHub Actions / Lint

Unexpected any. Specify a different type
query: {
bool: {
must: [],
@@ -405,12 +405,12 @@
mode: TraceAnalyticsMode,
filters: FilterType[],
query: string,
startTime: any,

Check warning on line 408 in public/components/trace_analytics/components/common/helper_functions.tsx

GitHub Actions / Lint

Unexpected any. Specify a different type
endTime: any,

Check warning on line 409 in public/components/trace_analytics/components/common/helper_functions.tsx

GitHub Actions / Lint

Unexpected any. Specify a different type
page?: string,
appConfigs: FilterType[] = []
) => {
const DSL: any = {

Check warning on line 413 in public/components/trace_analytics/components/common/helper_functions.tsx

GitHub Actions / Lint

Unexpected any. Specify a different type
query: {
bool: {
must: [],
@@ -677,3 +677,83 @@
return [];
}
};

export const InjectElementsIntoGrid = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the helper_functions file is getting too lang, it might be better to split this into a different file especially it's a custom react hook

also it's better to name hooks with use prefix to help linters

Suggested change
export const InjectElementsIntoGrid = (
export const useInjectElementsIntoGrid = (

and could you add some comments on top or make naming more specific, for example what elements are injected

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made new file public/components/trace_analytics/components/common/shared_components/component_helper_functions.tsx
And moved useInjectElementsIntoGrid = ( to it.
Added comment above
// Injects the size warning and Load buttons into the corners of EUI Data Grid

rowCount: number,
maxDisplayRows: number,
tracesTableMode: string,
loadMoreHandler?: () => void
) => {
useEffect(() => {
setTimeout(() => {
const toolbar = document.querySelector('.euiDataGrid__controls') as HTMLElement;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. it's better to avoid as assertions

Suggested change
const toolbar = document.querySelector('.euiDataGrid__controls') as HTMLElement;
const toolbar = document.querySelector<HTMLElement>('.euiDataGrid__controls');

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed all as assertions.

if (toolbar && rowCount > maxDisplayRows) {
toolbar.style.display = 'flex';
toolbar.style.alignItems = 'center';
toolbar.style.justifyContent = 'space-between';

let warningDiv = toolbar.querySelector('.trace-table-warning') as HTMLElement;
if (!warningDiv) {
warningDiv = document.createElement('div');
warningDiv.className = 'trace-table-warning';
warningDiv.style.marginLeft = 'auto';
warningDiv.style.color = '#a87900';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this visible in both dark and light mode? if not oui might have some class names for coloring

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2025-03-28 at 12 56 24 PM Screenshot 2025-03-28 at 12 56 52 PM

Warning Color appears visible in both modes

warningDiv.style.fontSize = '13px';
warningDiv.style.display = 'flex';
warningDiv.style.alignItems = 'center';
warningDiv.style.padding = '2px 8px';
warningDiv.style.borderRadius = '4px';
warningDiv.style.whiteSpace = 'nowrap';

const icon = document.createElement('span');
icon.className = 'euiIcon euiIcon--medium euiIcon--warning';
icon.setAttribute('aria-label', 'Warning');
icon.style.marginRight = '4px';

const strongElement = document.createElement('strong');
strongElement.textContent = `${maxDisplayRows}`;

const textSpan = document.createElement('span');
textSpan.appendChild(strongElement);
textSpan.appendChild(document.createTextNode(" results shown out of "));

Check failure on line 718 in public/components/trace_analytics/components/common/helper_functions.tsx

GitHub Actions / Lint

Replace `"·results·shown·out·of·"` with `'·results·shown·out·of·'`
textSpan.appendChild(document.createTextNode(` ${rowCount}`));

warningDiv.appendChild(icon);
warningDiv.appendChild(textSpan);

toolbar.appendChild(warningDiv);
}
}

const pagination = document.querySelector('.euiDataGrid__pagination') as HTMLElement;

if (tracesTableMode !== "traces") {

Check failure on line 730 in public/components/trace_analytics/components/common/helper_functions.tsx

GitHub Actions / Lint

Replace `"traces"` with `'traces'`
if (pagination) {
const existingLoadMoreButton = pagination.querySelector('.trace-table-load-more');
if (existingLoadMoreButton) {
existingLoadMoreButton.remove();
}
}
return;
}

if (pagination && loadMoreHandler) {
pagination.style.display = 'flex';
pagination.style.alignItems = 'center';
pagination.style.justifyContent = 'space-between';

let loadMoreButton = pagination.querySelector('.trace-table-load-more') as HTMLElement;
if (!loadMoreButton) {
loadMoreButton = document.createElement('button');
loadMoreButton.className = 'trace-table-load-more euiButtonEmpty euiButtonEmpty--text';
loadMoreButton.style.marginLeft = '12px';
loadMoreButton.innerText = 'Load more data';

loadMoreButton.onclick = () => loadMoreHandler();

pagination.appendChild(loadMoreButton);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DOM manipulation is hard to maintain and might break when OUI library changes. is it possible to use existing data grid functions to do this? for example https://oui.opensearch.org/1.19/#/tabular-content/data-grid-styling-and-control#additional-controls-in-the-toolbar

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, Data grid “Additional controls” only allows insertion to the left of the default controls. We are currently using that to add the Span/Trace selection and Fullscreen buttons. The component does not allow for buttons to be split into different locations to have the warning in the right corner. It also doesn’t allow for manipulation of the pagination to insert the “Load more” button at the bottom right. With current UX requirements, I think DOM manipulation is the best solution; pending a refactor / adjustments of the OUI component.

}
}
}, 100);
}, [rowCount, tracesTableMode, loadMoreHandler]);

Check warning on line 758 in public/components/trace_analytics/components/common/helper_functions.tsx

GitHub Actions / Lint

React Hook useEffect has a missing dependency: 'maxDisplayRows'. Either include it or remove the dependency array
};
Original file line number Diff line number Diff line change
@@ -13,18 +13,16 @@
EuiFlexItem,
EuiHighlight,
EuiIcon,
EuiLoadingContent,
EuiLoadingSpinner,
EuiOverlayMask,
EuiPopover,
EuiPopoverTitle,
EuiSelectable,
EuiText,
EuiTextColor,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@osd/i18n';
import React, { useMemo, useState } from 'react';
import { NoMatchMessage } from '../helper_functions';
import { InjectElementsIntoGrid, NoMatchMessage } from '../helper_functions';
import {
TRACE_TABLE_OPTIONS,
TRACE_TABLE_TITLES,
@@ -107,10 +105,10 @@
noMatchMessageSize = 'xl',
defaultHeight = '500px',
visibleColumns,
isTableDataLoading,

Check failure on line 108 in public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx

GitHub Actions / Lint

'isTableDataLoading' is defined but never used. Allowed unused args must match /^_/u
tracesTableMode,
setTracesTableMode,
maxTraces,

Check failure on line 111 in public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx

GitHub Actions / Lint

'maxTraces' is defined but never used. Allowed unused args must match /^_/u
setMaxTraces,
}) => {
const defaultVisibleColumns = useMemo(() => {
@@ -127,14 +125,18 @@
const [isPopoverOpen, setIsPopoverOpen] = useState(false);

const displayedRowCount = rowCount > MAX_DISPLAY_ROWS ? MAX_DISPLAY_ROWS : rowCount;
const isRowCountLimited = rowCount > MAX_DISPLAY_ROWS;

Check failure on line 128 in public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx

GitHub Actions / Lint

'isRowCountLimited' is assigned a value but never used. Allowed unused vars must match /^_/u

const tableOptions = tracesTableMode
? TRACE_TABLE_OPTIONS.map((obj) =>
obj.key === tracesTableMode ? { ...obj, checked: 'on' } : obj

Check failure on line 132 in public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx

GitHub Actions / Lint

Insert `··`
)

Check failure on line 133 in public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx

GitHub Actions / Lint

Insert `··`
: [];

InjectElementsIntoGrid(rowCount, MAX_DISPLAY_ROWS, tracesTableMode ?? "", () => {

Check failure on line 136 in public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx

GitHub Actions / Lint

Replace `""` with `''`
setMaxTraces((prevMax: number) => Math.min(prevMax + 500, MAX_DISPLAY_ROWS));
});

const disableInteractions = useMemo(() => isFullScreen, [isFullScreen]);

const tableModeSelector =
@@ -145,29 +147,21 @@
panelStyle={{ width: '350px' }}
button={
<EuiFlexGroup alignItems="center" gutterSize="s" direction="row">

{isTableDataLoading && (
<EuiFlexGroup alignItems="center" gutterSize="xs" direction="row">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="m" />
<EuiText size="s">
<EuiTextColor
color="success"
onClick={() => setIsPopoverOpen(!isPopoverOpen)}
style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}
>
{TRACE_TABLE_TITLES[tracesTableMode]} ({displayedRowCount})
<EuiIcon type="arrowDown" size="s" color="success" style={{ marginLeft: 4 }} />
</EuiTextColor>
</EuiText>
</EuiFlexItem>
)}
</EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
size="xs"
onClick={() => setIsPopoverOpen(!isPopoverOpen)}
iconType="arrowDown"
data-test-subj="traceTableModeSelector"
color="text"
>
{TRACE_TABLE_TITLES[tracesTableMode]} ({displayedRowCount})
{isRowCountLimited && (
<EuiToolTip
content={`Actual hits: ${rowCount}. Only ${MAX_DISPLAY_ROWS} can be displayed.`}
>
<EuiIcon type="iInCircle" color="subdued" size="s" style={{ marginLeft: 6 }} />
</EuiToolTip>
)}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
}
@@ -188,7 +182,7 @@
)}
onChange={(newOptions) => {
const selectedMode = newOptions.find((option) => option.checked === 'on')?.key;
if (selectedMode) {
if (selectedMode && selectedMode !== tracesTableMode) {
setTracesTableMode(selectedMode);
sessionStorage.setItem(TRACE_TABLE_TYPE_KEY, selectedMode);
}
@@ -200,45 +194,39 @@
</EuiPopover>
) : null;

const LoadMoreButton =
tracesTableMode === 'traces' && maxTraces < MAX_DISPLAY_ROWS ? (
<EuiButtonEmpty
size="xs"
onClick={() => {
setMaxTraces((prevMax) => Math.min(prevMax + 500, MAX_DISPLAY_ROWS));
}}
key="loadMore"
color="text"
data-test-subj="loadMoreButton"
>
Load more
</EuiButtonEmpty>
) : null;
const toolbarControls = useMemo(() => {
const controls = [];

if (tableModeSelector) {
controls.push(tableModeSelector);
}

const toolbarControls = useMemo(
() => [
...(tableModeSelector ? [tableModeSelector] : []),
LoadMoreButton,
<EuiButtonEmpty
size="xs"
onClick={() => setIsFullScreen((prev) => !prev)}
key="fullScreen"
color="text"
if (!["all_spans", "root_spans", "service_entry_spans"].includes(tracesTableMode ?? "")) {
controls.push(
<EuiButtonEmpty
size="xs"
onClick={() => setIsFullScreen((prev) => !prev)}
key="fullScreen"
color="text"
iconType={isFullScreen ? 'cross' : 'fullScreen'}
data-test-subj="fullScreenButton"
>
{isFullScreen
data-test-subj="fullScreenButton"
>
{isFullScreen
? i18n.translate('toolbarControls.exitFullScreen', {
defaultMessage: 'Exit full screen',
})
})
: i18n.translate('toolbarControls.fullScreen', {
defaultMessage: 'Full screen',
})}
</EuiButtonEmpty>,
...toolbarButtons,
],
[isFullScreen, toolbarButtons, tracesTableMode]
);
})}
</EuiButtonEmpty>
);
}

controls.push(...toolbarButtons);

return controls;
}, [isFullScreen, toolbarButtons, tracesTableMode]);

Check warning on line 228 in public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx

GitHub Actions / Lint

React Hook useMemo has a missing dependency: 'tableModeSelector'. Either include it or remove the dependency array


const gridStyle = useMemo(
() => ({
@@ -253,11 +241,7 @@
[]
);

return false ? ( //isTableDataLoading DELETE ADAM
<div>
<EuiLoadingContent lines={4} />
</div>
) : (
return (
<>
<FullScreenWrapper isFullScreen={isFullScreen} onClose={() => setIsFullScreen(false)}>
<div className={isFullScreen ? 'full-wrapper' : 'normal-wrapper'}>
Original file line number Diff line number Diff line change
@@ -151,9 +151,6 @@ export const getTracesQuery = (
if (doc.containsKey('traceGroupFields.durationInNanos') && !doc['traceGroupFields.durationInNanos'].empty) {
return Math.round(doc['traceGroupFields.durationInNanos'].value / 10000) / 100.0
}
if (doc.containsKey('durationInNanos') && !doc['durationInNanos'].empty) {
return Math.round(doc['durationInNanos'].value / 10000) / 100.0
}

return 0
`,
@@ -176,19 +173,7 @@ export const getTracesQuery = (
},
last_updated: {
max: {
script: {
source: `
if (doc.containsKey('traceGroupFields.endTime') && !doc['traceGroupFields.endTime'].empty) {
return doc['traceGroupFields.endTime'].value
}
if (doc.containsKey('endTime') && !doc['endTime'].empty) {
return doc['endTime'].value
}

return 0
`,
lang: 'painless',
},
field: 'traceGroupFields.endTime',
},
},
},
Loading