Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 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
f198a82
Merge branch 'main' of https://github.com/elastic/kibana into 251104-…
iblancof Feb 3, 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
facb4ae
Merge branch '4863-deliverable-open-in-discover-in-logs-traces-discov…
iblancof Feb 3, 2026
fc4b1da
Extract getLinkActionProps
iblancof Feb 3, 2026
4f52975
Merge branch 'main' into 251104-discovertraces-update-additional-over…
iblancof Feb 4, 2026
6e1e393
Remove unused fn
iblancof Feb 4, 2026
54a849d
Add a11y tableCaption to EuiMemoryTable
iblancof Feb 5, 2026
043ace9
Update Errors table to open in discover tab
iblancof Feb 5, 2026
813d0c6
Merge branch 'main' of https://github.com/elastic/kibana into 251104-…
iblancof Feb 5, 2026
71059f9
Create DiscoverEsqlLink
iblancof Feb 5, 2026
4c06043
Extract createSpanNameWhereClause
iblancof Feb 5, 2026
1adf3e2
Use DiscoverEsqlLink in span links columns
iblancof Feb 5, 2026
cfc3e36
Use DiscoverEsqlLink in errors columns
iblancof Feb 5, 2026
022cbd2
Remove duplicated imports
iblancof Feb 5, 2026
f7dba50
Merge branch 'main' into 251104-discovertraces-update-additional-over…
iblancof Feb 6, 2026
1a2c955
Merge branch 'main' into 251104-discovertraces-update-additional-over…
iblancof Feb 6, 2026
21316a2
Merge branch 'main' into 251104-discovertraces-update-additional-over…
iblancof Feb 9, 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 React from 'react';
import { render, screen, fireEvent, createEvent } from '@testing-library/react';
import { render, screen, fireEvent } 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 @@ -72,83 +72,6 @@ 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 @@ -10,6 +10,7 @@
import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui';
import type { IconType } from '@elastic/eui';
import { getLinkActionProps } from '../utils/link_action';

interface BaseAction {
icon: IconType;
Expand All @@ -27,10 +28,6 @@ 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 @@ -39,20 +36,7 @@ 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 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,
};
const buttonProps = getLinkActionProps({ href, onClick });

return (
<EuiFlexItem grow={false} key={action.id ?? idx} id={action.id}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import type { MouseEvent, MouseEventHandler } from 'react';

interface LinkActionProps {
href?: string;
onClick?: () => void;
}

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

/**
* Creates `href` and `onClick` props that preserve native link behavior for
* right-clicks and modifier-clicks, while allowing a handler to run on plain
* left-click (preventing navigation when `href` is present).
*/
export function getLinkActionProps({ href, onClick }: LinkActionProps): {
href?: string;
onClick?: MouseEventHandler;
} {
const handleClick: MouseEventHandler | undefined = onClick
? (e) => {
// 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;

return {
href,
onClick: handleClick,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { getLinkActionProps } from '.';

describe('link_action_utils', () => {
describe('getLinkActionProps', () => {
it('returns empty props when neither href nor onClick are provided', () => {
expect(getLinkActionProps({})).toEqual({});
});

it('returns only href when href is provided without onClick', () => {
expect(getLinkActionProps({ href: '/app/discover' })).toEqual({ href: '/app/discover' });
});

it('returns only onClick when onClick is provided without href', () => {
const onClick = jest.fn();
const props = getLinkActionProps({ onClick });
expect(props.href).toBeUndefined();
expect(typeof props.onClick).toBe('function');

props.onClick?.({} as any);
expect(onClick).toHaveBeenCalledTimes(1);
});

it('prefers onClick over href on plain left click', () => {
const onClick = jest.fn();
const props = getLinkActionProps({ href: '/app/discover', onClick });

const preventDefault = jest.fn();
props.onClick?.({
button: 0,
metaKey: false,
ctrlKey: false,
shiftKey: false,
altKey: false,
preventDefault,
} as any);

expect(onClick).toHaveBeenCalledTimes(1);
expect(preventDefault).toHaveBeenCalledTimes(1);
});

it('does not intercept modifier click when href is present', () => {
const onClick = jest.fn();
const props = getLinkActionProps({ href: '/app/discover', onClick });

const preventDefault = jest.fn();
props.onClick?.({
button: 0,
ctrlKey: true,
metaKey: false,
shiftKey: false,
altKey: false,
preventDefault,
} as any);

expect(onClick).not.toHaveBeenCalled();
expect(preventDefault).not.toHaveBeenCalled();
});

it('does not intercept middle click when href is present', () => {
const onClick = jest.fn();
const props = getLinkActionProps({ href: '/app/discover', onClick });

const preventDefault = jest.fn();
props.onClick?.({
button: 1,
metaKey: false,
ctrlKey: false,
shiftKey: false,
altKey: false,
preventDefault,
} as any);

expect(onClick).not.toHaveBeenCalled();
expect(preventDefault).not.toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React from 'react';
import { createEvent, fireEvent, render } from '@testing-library/react';
import '@testing-library/jest-dom';

import { DiscoverEsqlLink } from '.';
import { useDiscoverLinkAndEsqlQuery } from '../../../../../hooks/use_discover_link_and_esql_query';
import { useDocViewerExtensionActionsContext } from '../../../../../hooks/use_doc_viewer_extension_actions';

jest.mock('../../../../../hooks/use_discover_link_and_esql_query', () => ({
useDiscoverLinkAndEsqlQuery: jest.fn(),
}));

jest.mock('../../../../../hooks/use_doc_viewer_extension_actions', () => ({
useDocViewerExtensionActionsContext: jest.fn(),
}));

describe('DiscoverEsqlLink', () => {
const indexPattern = 'apm-*';
const whereClause = undefined;
const tabLabel = 'Tab label';
const dataTestSubj = 'discoverEsqlLink';

beforeEach(() => {
jest.clearAllMocks();
(useDocViewerExtensionActionsContext as jest.Mock).mockReturnValue(undefined);
(useDiscoverLinkAndEsqlQuery as jest.Mock).mockReturnValue({
discoverUrl: undefined,
esqlQueryString: undefined,
});
});

it('renders children without a link when href and openInNewTab are unavailable', () => {
const { getByText, queryByTestId } = render(
<DiscoverEsqlLink
indexPattern={indexPattern}
whereClause={whereClause}
tabLabel={tabLabel}
dataTestSubj={dataTestSubj}
>
Child
</DiscoverEsqlLink>
);

expect(getByText('Child')).toBeInTheDocument();
expect(queryByTestId(dataTestSubj)).not.toBeInTheDocument();
});

it('renders a link when discoverUrl is available', () => {
(useDiscoverLinkAndEsqlQuery as jest.Mock).mockReturnValue({
discoverUrl: '/app/discover#/?_a=1',
esqlQueryString: undefined,
});

const { getByTestId } = render(
<DiscoverEsqlLink
indexPattern={indexPattern}
whereClause={whereClause}
tabLabel={tabLabel}
dataTestSubj={dataTestSubj}
>
Child
</DiscoverEsqlLink>
);

expect(getByTestId(dataTestSubj)).toHaveAttribute('href', '/app/discover#/?_a=1');
});

it('calls openInNewTab on plain left click when esqlQueryString is available', () => {
const openInNewTab = jest.fn();
(useDocViewerExtensionActionsContext as jest.Mock).mockReturnValue({ openInNewTab });
(useDiscoverLinkAndEsqlQuery as jest.Mock).mockReturnValue({
discoverUrl: '/app/discover#/?_a=1',
esqlQueryString: 'FROM apm-* | WHERE true',
});

const { getByTestId } = render(
<DiscoverEsqlLink
indexPattern={indexPattern}
whereClause={whereClause}
tabLabel={tabLabel}
dataTestSubj={dataTestSubj}
>
Child
</DiscoverEsqlLink>
);

const link = getByTestId(dataTestSubj);
const clickEvent = createEvent.click(link, { button: 0 });
fireEvent(link, clickEvent);

expect(clickEvent.defaultPrevented).toBe(true);
expect(openInNewTab).toHaveBeenCalledWith({
query: { esql: 'FROM apm-* | WHERE true' },
tabLabel,
});
});

it('does not intercept modifier clicks when href is present', () => {
const openInNewTab = jest.fn();
(useDocViewerExtensionActionsContext as jest.Mock).mockReturnValue({ openInNewTab });
(useDiscoverLinkAndEsqlQuery as jest.Mock).mockReturnValue({
discoverUrl: '/app/discover#/?_a=1',
esqlQueryString: 'FROM apm-* | WHERE true',
});

const { getByTestId } = render(
<DiscoverEsqlLink
indexPattern={indexPattern}
whereClause={whereClause}
tabLabel={tabLabel}
dataTestSubj={dataTestSubj}
>
Child
</DiscoverEsqlLink>
);

const link = getByTestId(dataTestSubj);
const ctrlClickEvent = createEvent.click(link, { button: 0, ctrlKey: true });
fireEvent(link, ctrlClickEvent);

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