Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
e5e53ba
Modify section actions to allow both href and click
iblancof Jan 28, 2026
e1180a0
Merge branch 'main' of https://github.com/elastic/kibana into 4863-de…
iblancof Jan 29, 2026
09598ef
Create useDocViewerExtensionActions
iblancof Jan 29, 2026
53d922c
Pass actions to overview
iblancof Jan 29, 2026
2bc455a
Create useOpenInDiscoverSectionAction
iblancof Jan 29, 2026
06e6108
Create useDiscoverLinkAndEsqlQuery
iblancof Jan 29, 2026
e519159
Change useOpenInDiscoverSectionAction params
iblancof Jan 29, 2026
a8a5329
Use new hooks in SimilarSpans
iblancof Jan 29, 2026
4985d84
Use new hooks in TraceContextLogEvents
iblancof Jan 29, 2026
44ffbe0
ADD TODO in overview
iblancof Jan 29, 2026
792b982
Merge branch 'main' of https://github.com/elastic/kibana into 4863-de…
iblancof Jan 29, 2026
e78a254
Use new hooks in ErrorsTable
iblancof Jan 29, 2026
1f61861
Remove unused constant
iblancof Jan 29, 2026
2435455
Update dataTestSubj for errors
iblancof Jan 29, 2026
889ce4c
Use new hooks in SpanLinks
iblancof Jan 29, 2026
99eb581
Pass actions to logs overview
iblancof Jan 29, 2026
8b0736f
Use new hooks in SimilarErrors
iblancof Jan 29, 2026
8b5c96f
Merge branch 'main' of https://github.com/elastic/kibana into 4863-de…
iblancof Jan 30, 2026
3488f4e
Move hooks
iblancof Jan 30, 2026
01f3390
Pass actions into waterfall flyout overviews
iblancof Jan 30, 2026
6715ced
Fix typing issue in SpanLinks
iblancof Jan 30, 2026
9753cf3
Set actions at a higher type level
iblancof Jan 30, 2026
9656e0b
Add tests for useDiscoverLinkAndEsqlQuery
iblancof Jan 30, 2026
caeb605
Add tests for useOpenInDiscoverSectionAction
iblancof Jan 30, 2026
9329caf
Changes from node scripts/lint_ts_projects --fix
kibanamachine Jan 30, 2026
063567d
Changes from node scripts/regenerate_moon_projects.js --update
kibanamachine Jan 30, 2026
8816579
Merge branch 'main' of https://github.com/elastic/kibana into 4863-de…
iblancof Feb 2, 2026
a44704b
Use new hooks in TraceWaterfall
iblancof Feb 2, 2026
12b8674
Update import to avoid circular dep
iblancof Feb 2, 2026
c6b8dc6
Changes from node scripts/lint_ts_projects --fix
kibanamachine Feb 2, 2026
01ae64c
Changes from node scripts/regenerate_moon_projects.js --update
kibanamachine Feb 2, 2026
f3f2193
Update constant names
iblancof Feb 2, 2026
23b4009
Merge branch '4863-deliverable-open-in-discover-in-logs-traces-discov…
iblancof Feb 2, 2026
61ac197
Update import strategy for hooks
iblancof Feb 2, 2026
2251b8f
Update constant name
iblancof Feb 2, 2026
1dd08d8
Refactor useOpenInDiscoverSectionAction
iblancof Feb 2, 2026
dd5486c
Merge branch 'main' of https://github.com/elastic/kibana into 4863-de…
iblancof Feb 3, 2026
5e19fda
Simplify buttonProps
iblancof Feb 3, 2026
9c13712
Memoize actions
iblancof Feb 3, 2026
37423a3
Simplify useDocViewerExtensionActions return
iblancof Feb 3, 2026
12e0e05
Place actionId before its usage
iblancof Feb 3, 2026
338a521
Remove actions from DocViewRenderProps
iblancof Feb 3, 2026
bf57111
Fix import in UseDocViewerExtensionActionsParams
iblancof Feb 3, 2026
d514da9
Update actions prop name
iblancof Feb 3, 2026
b77c539
Add actions to generic doc viewer
iblancof Feb 3, 2026
9fcb91f
Update actions prop name
iblancof Feb 3, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import type { DataView } from '@kbn/data-views-plugin/public';
import type { AggregateQuery, Query } from '@kbn/es-query';
import type { AggregateQuery, Query, TimeRange } from '@kbn/es-query';
import type { DataTableRecord, DataTableColumnsMeta } from '@kbn/discover-utils/types';
import type { RestorableStateProviderProps } from '@kbn/restorable-state';
import type { ReactElement } from 'react';
Expand Down Expand Up @@ -43,6 +43,15 @@ export type DocViewFilterFn = (
mode: '+' | '-'
) => void;

export interface DocViewActions {
openInNewTab?: (params: {
query?: Query | AggregateQuery;
tabLabel?: string;
timeRange?: TimeRange;
}) => void;
updateESQLQuery?: (queryOrUpdater: string | ((prevQuery: string) => string)) => void;
}
Comment on lines +46 to +53
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These actions are too Discover specific to add on all doc views. Can we instead make doc views that want to use them extend a common props interface with actions?: DocViewActions; to make it opt in?

Also unfortunate we need to duplicate types from Discover, but that's more an issue on our end. We originally assumed contextual components would live directly in the Discover codebase.

Copy link
Copy Markdown
Contributor Author

@iblancof iblancof Feb 3, 2026

Choose a reason for hiding this comment

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

Hey Davis, thanks so much for the feedback.

These actions are too Discover specific to add on all doc views. Can we instead make doc views that want to use them extend a common props interface with actions?: DocViewActions; to make it opt in?

I actually had the same question initially when adding actions directly to DocViewRenderProps, but I decided to include it after seeing that filters, onAddColumn, and onRemoveColumn were already defined there, and those also feel quite tightly coupled to Discover. Are those functions meant to be used outside of Discover as well? Maybe I’m missing some context here.

That said, I’ll follow your recommendation since you suggested it, but I’d still love to better understand what makes actions different from the other functions I mentioned above.

Also unfortunate we need to duplicate types from Discover, but that's more an issue on our end. We originally assumed contextual components would live directly in the Discover codebase.

Yeah, I did run into circular deps at some point because of types. We could probably “just” extract them, but based on what you mentioned about the initial assumption, I get the sense that the issue goes beyond that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for updating it!

Are those functions meant to be used outside of Discover as well?

While it's not the way I'd model it personally tbh, yes they can be. They're for integrating with any consumers supporting Unified Search or displaying tabular results (e.g. Timeline, Streams, etc.). Also worth noting Unified Doc Viewer is quite old and carries around some unfortunate tech debt.

We could probably “just” extract them, but based on what you mentioned about the initial assumption, I get the sense that the issue goes beyond that.

I think extracting them is probably an acceptable solution, it just hadn't been an issue until more recently so we never did. I'm also ok with components living outside the Discover codebase, just an incorrect assumption when we first built things.


export interface DocViewRenderProps {
hit: DataTableRecord;
dataView: DataView;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

export type {
DocView,
DocViewActions,
DocViewFilterFn,
DocViewRenderProps,
DocViewerComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@
*/

export type { ElasticRequestState } from '.';
export type { DocViewFilterFn, DocViewRenderProps, DocView, DocViewerComponent } from './src/types';
export type {
DocViewFilterFn,
DocViewRenderProps,
DocView,
DocViewerComponent,
DocViewActions,
} from './src/types';
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
UnifiedDocViewerLogsOverview,
type UnifiedDocViewerLogsOverviewApi,
} from '@kbn/unified-doc-viewer-plugin/public';
import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types';
import type { DocViewRenderProps, DocViewActions } from '@kbn/unified-doc-viewer/types';
import React, { useEffect, useRef, useState } from 'react';
import type { BehaviorSubject } from 'rxjs';
import { filter, skip } from 'rxjs';
Expand Down Expand Up @@ -67,6 +67,7 @@ export const createGetDocViewer =
logsAIInsightFeature={logsAIInsightFeature}
streamsFeature={streamsFeature}
indexes={indexes}
docViewActions={params.actions}
{...props}
/>
);
Expand All @@ -84,6 +85,7 @@ interface LogOverviewTabProps extends DocViewRenderProps {
logsAIInsightFeature: ObservabilityLogsAIInsightFeature | undefined;
streamsFeature: ObservabilityStreamsFeature | undefined;
indexes: ObservabilityIndexes;
docViewActions?: DocViewActions;
}

const LogOverviewTab = ({
Expand All @@ -92,6 +94,7 @@ const LogOverviewTab = ({
logsAIInsightFeature,
streamsFeature,
indexes,
docViewActions,
...props
}: LogOverviewTabProps) => {
const [logsOverviewApi, setLogsOverviewApi] = useState<UnifiedDocViewerLogsOverviewApi | null>(
Expand All @@ -102,6 +105,7 @@ const LogOverviewTab = ({
return (
<UnifiedDocViewerLogsOverview
{...props}
docViewActions={docViewActions}
ref={setLogsOverviewApi}
renderAIAssistant={logsAIAssistantFeature?.render}
renderAIInsight={logsAIInsightFeature?.render}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ export const createGetDocViewer =
title: tabTitle,
order: 0,
render: (props) => (
<UnifiedDocViewerObservabilityGenericOverview {...props} indexes={indexes} />
<UnifiedDocViewerObservabilityGenericOverview
{...props}
indexes={indexes}
docViewActions={params.actions}
/>
),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ export const createGetDocViewer =
title: tabTitle,
order: 0,
render: (props) => (
<UnifiedDocViewerObservabilityTracesOverview {...props} indexes={indexes} />
<UnifiedDocViewerObservabilityTracesOverview
{...props}
indexes={indexes}
docViewActions={params.actions}
/>
),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
*/

import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { render, screen, fireEvent, createEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';
import { ContentFrameworkSection, type ContentFrameworkSectionProps } from './section';

Expand Down Expand Up @@ -71,6 +72,83 @@ describe('ContentFrameworkSection', () => {
expect(defaultProps.actions?.[1].onClick).toHaveBeenCalled();
});

it('prefers onClick over href on plain left click', () => {
const onClick = jest.fn();
render(
<ContentFrameworkSection
{...defaultProps}
actions={[
{
icon: 'discoverApp',
ariaLabel: 'Open in Discover',
dataTestSubj: 'unifiedDocViewerSectionActionButton-openInDiscover',
label: 'Open in Discover',
href: '/app/discover',
onClick,
},
]}
/>
);

const button = screen.getByTestId('unifiedDocViewerSectionActionButton-openInDiscover');
const clickEvent = createEvent.click(button, { button: 0 });
fireEvent(button, clickEvent);

expect(onClick).toHaveBeenCalledTimes(1);
expect(clickEvent.defaultPrevented).toBe(true);
});

it('does not intercept modifier click when href is present', () => {
const onClick = jest.fn();
render(
<ContentFrameworkSection
{...defaultProps}
actions={[
{
icon: 'discoverApp',
ariaLabel: 'Open in Discover',
dataTestSubj: 'unifiedDocViewerSectionActionButton-openInDiscover',
label: 'Open in Discover',
href: '/app/discover',
onClick,
},
]}
/>
);

const button = screen.getByTestId('unifiedDocViewerSectionActionButton-openInDiscover');
const ctrlClickEvent = createEvent.click(button, { button: 0, ctrlKey: true });
fireEvent(button, ctrlClickEvent);

expect(onClick).not.toHaveBeenCalled();
expect(ctrlClickEvent.defaultPrevented).toBe(false);
});

it('does not intercept middle click when href is present', () => {
const onClick = jest.fn();
render(
<ContentFrameworkSection
{...defaultProps}
actions={[
{
icon: 'discoverApp',
ariaLabel: 'Open in Discover',
dataTestSubj: 'unifiedDocViewerSectionActionButton-openInDiscoverIcon',
href: '/app/discover',
onClick,
},
]}
/>
);

const button = screen.getByTestId('unifiedDocViewerSectionActionButton-openInDiscoverIcon');
const middleClickEvent = createEvent.click(button, { button: 1 });
fireEvent(button, middleClickEvent);

expect(onClick).not.toHaveBeenCalled();
expect(middleClickEvent.defaultPrevented).toBe(false);
});

it('renders children inside the panel', () => {
render(<ContentFrameworkSection {...defaultProps} />);
expect(screen.getByText('Section children')).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ interface BaseAction {
}

export type Action =
| (BaseAction & { onClick: () => void; href?: never })
| (BaseAction & { href: string; onClick?: never });
| (BaseAction & { onClick: () => void; href?: string })
| (BaseAction & { href: string; onClick?: () => void });

export interface SectionActionsProps {
actions: Action[];
}

function isPlainLeftClick(e: React.MouseEvent) {
return e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;
}

export const SectionActions = ({ actions }: SectionActionsProps) => {
if (!actions.length) return null;
const size = 'xs';
Expand All @@ -35,7 +39,20 @@ export const SectionActions = ({ actions }: SectionActionsProps) => {
<EuiFlexGroup gutterSize="s" justifyContent="flexEnd" alignItems="center">
{actions.map((action, idx) => {
const { icon, ariaLabel, dataTestSubj, label, onClick, href } = action;
const buttonProps = onClick ? { onClick } : { href };
const handleClick = onClick
? (e: React.MouseEvent) => {
// If we have an href, keep native link behaviour for right clicks and modifier clicks.
// Plain left click should run the provided handler instead.
if (href && !isPlainLeftClick(e)) return;
if (href) e.preventDefault();
onClick();
}
: undefined;

const buttonProps = {
href,
onClick: handleClick,
};

return (
<EuiFlexItem grow={false} key={action.id ?? idx} id={action.id}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
import type { LogDocument, ObservabilityIndexes } from '@kbn/discover-utils/src';
import { getStacktraceFields } from '@kbn/discover-utils/src';
import { css } from '@emotion/react';
import type { DocViewActions } from '@kbn/unified-doc-viewer/src/services/types';
import { LogsOverviewHeader } from './logs_overview_header';
import { FieldActionsProvider } from '../../hooks/use_field_actions';
import { getUnifiedDocViewerServices } from '../../plugin';
Expand All @@ -39,6 +40,7 @@ import { TraceWaterfall } from '../observability/traces/components/trace_waterfa
import { DataSourcesProvider } from '../../hooks/use_data_sources';
import { SimilarErrors } from './sub_components/similar_errors';
import { hasErrorFields } from './utils/has_error_fields';
import { DocViewerExtensionActionsProvider } from '../../hooks/use_doc_viewer_extension_actions';

export type LogsOverviewProps = DocViewRenderProps & {
renderAIAssistant?: ObservabilityLogsAIAssistantFeature['render'];
Expand All @@ -47,6 +49,7 @@ export type LogsOverviewProps = DocViewRenderProps & {
renderFlyoutStreamProcessingLink?: ObservabilityStreamsFeature['renderFlyoutStreamProcessingLink'];
indexes: ObservabilityIndexes;
showTraceWaterfall?: boolean;
docViewActions?: DocViewActions;
};

export interface LogsOverviewApi {
Expand All @@ -69,6 +72,7 @@ export const LogsOverview = forwardRef<LogsOverviewApi, LogsOverviewProps>(
renderFlyoutStreamProcessingLink,
indexes,
showTraceWaterfall = true,
docViewActions,
},
ref
) => {
Expand Down Expand Up @@ -131,24 +135,28 @@ export const LogsOverview = forwardRef<LogsOverviewApi, LogsOverviewProps>(
dataView={dataView}
/>
<DataSourcesProvider indexes={indexes}>
{showSimilarErrors ? <SimilarErrors hit={hit} /> : null}
<div>{renderFlyoutStreamField && renderFlyoutStreamField({ dataView, doc: hit })}</div>
<LogsOverviewDegradedFields ref={qualityIssuesSectionRef} rawDoc={hit.raw} />
{isStacktraceAvailable && (
<LogsOverviewStacktraceSection
ref={stackTraceSectionRef}
hit={hit}
dataView={dataView}
/>
)}
{traceId && showTraceWaterfall ? (
<TraceWaterfall
traceId={traceId}
docId={parsedDoc[TRANSACTION_ID_FIELD] || parsedDoc[SPAN_ID_FIELD]}
serviceName={parsedDoc[SERVICE_NAME_FIELD]}
dataView={dataView}
/>
) : null}
<DocViewerExtensionActionsProvider actions={docViewActions}>
{showSimilarErrors ? <SimilarErrors hit={hit} /> : null}
<div>
{renderFlyoutStreamField && renderFlyoutStreamField({ dataView, doc: hit })}
</div>
<LogsOverviewDegradedFields ref={qualityIssuesSectionRef} rawDoc={hit.raw} />
{isStacktraceAvailable && (
<LogsOverviewStacktraceSection
ref={stackTraceSectionRef}
hit={hit}
dataView={dataView}
/>
)}
{traceId && showTraceWaterfall ? (
<TraceWaterfall
traceId={traceId}
docId={parsedDoc[TRANSACTION_ID_FIELD] || parsedDoc[SPAN_ID_FIELD]}
serviceName={parsedDoc[SERVICE_NAME_FIELD]}
dataView={dataView}
/>
) : null}
</DocViewerExtensionActionsProvider>
</DataSourcesProvider>
{LogsOverviewAIAssistant && <LogsOverviewAIAssistant doc={hit} />}
<EuiSpacer size="m" />
Expand Down
Loading
Loading