diff --git a/packages/kbn-babel-preset/styled_components_files.js b/packages/kbn-babel-preset/styled_components_files.js
index 2cd85bf9e3657..d885470c919fd 100644
--- a/packages/kbn-babel-preset/styled_components_files.js
+++ b/packages/kbn-babel-preset/styled_components_files.js
@@ -176,7 +176,6 @@ module.exports = {
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]and_or_badge[\/\\]rounded_badge_antenna.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]auto_download[\/\\]auto_download.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]conditions_table[\/\\]index.stories.tsx/,
- /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]drag_and_drop[\/\\]draggable_wrapper.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]drag_and_drop[\/\\]droppable_wrapper.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]drag_and_drop[\/\\]provider_container.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]empty_value[\/\\]empty_value.test.tsx/,
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/__snapshots__/cell_actions_renderer.test.tsx.snap b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/__snapshots__/cell_actions_renderer.test.tsx.snap
new file mode 100644
index 0000000000000..d933bda4164e5
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/__snapshots__/cell_actions_renderer.test.tsx.snap
@@ -0,0 +1,24 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`cell actions renderer rendering it renders theCellActionsRenderer 1`] = `
+
+
+
+
+
+ A child of this
+
+
+
+
+
+`;
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/cell_actions_renderer.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/cell_actions_renderer.test.tsx
new file mode 100644
index 0000000000000..3df8ba94fa906
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/cell_actions_renderer.test.tsx
@@ -0,0 +1,268 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { render } from '@testing-library/react';
+import React from 'react';
+import { TableId } from '@kbn/securitysolution-data-table';
+import { TestProviders } from '../../mock';
+import { TimelineId } from '../../../../common/types';
+import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants';
+
+import { CellActionsRenderer } from './cell_actions_renderer';
+
+jest.mock('../../lib/kibana');
+
+const MockSecurityCellActions = jest.fn(({ children }: { children: React.ReactNode }) => (
+ {children}
+));
+jest.mock('.', () => ({
+ ...jest.requireActual('.'),
+ SecurityCellActions: (params: { children: React.ReactNode }) => MockSecurityCellActions(params),
+}));
+
+const mockSourcererScopeId = 'testSourcererScopeId';
+jest.mock('../../../helpers', () => ({
+ getSourcererScopeId: jest.fn(() => mockSourcererScopeId),
+}));
+
+describe('cell actions renderer', () => {
+ describe('rendering', () => {
+ test('it renders theCellActionsRenderer', () => {
+ const { container } = render(
+
+ {'A child of this'}
+
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ });
+ });
+
+ describe('CellActionsRenderer', () => {
+ test('it works with just an id, field, and value and is some value', () => {
+ const field = 'some-field';
+ const value = 'some value';
+ const { getByText } = render(
+
+
+
+ );
+ expect(getByText(value)).toBeInTheDocument();
+ });
+
+ test('it returns null if value is undefined', () => {
+ const { container } = render();
+ expect(container.firstChild).toBeNull();
+ });
+
+ test('it returns null if value is null', () => {
+ const { container } = render();
+ expect(container.firstChild).toBeNull();
+ });
+
+ test('it renders content with tooltip when tooltip is not explicitly provided', () => {
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('a field value')).toBeInTheDocument();
+ });
+
+ test('it renders content with custom tooltip when string is provided', () => {
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('a field value')).toBeInTheDocument();
+ });
+
+ test('it renders content with custom tooltip when element is provided', () => {
+ const { getByText } = render(
+
+ {'tooltip'}}
+ value="a field value"
+ />
+
+ );
+
+ expect(getByText('a field value')).toBeInTheDocument();
+ });
+
+ test('it does NOT render a tooltip when tooltipContent is null', () => {
+ const { queryByTestId } = render(
+
+
+
+ );
+
+ expect(queryByTestId('source.bytes-tooltip')).not.toBeInTheDocument();
+ });
+ });
+
+ describe('CellActionsRenderer functionality', () => {
+ const field = 'host.name';
+ const value = '12345';
+ const data = { field, value };
+
+ const scopeIdsWithHoverActions = [
+ undefined,
+ TimelineId.active,
+ TableId.alternateTest,
+ TimelineId.casePage,
+ TableId.alertsOnAlertsPage,
+ TableId.alertsOnRuleDetailsPage,
+ TableId.hostsPageEvents,
+ TableId.hostsPageSessions,
+ TableId.kubernetesPageSessions,
+ TableId.networkPageEvents,
+ TimelineId.test,
+ TableId.usersPageEvents,
+ ];
+
+ const scopeIdsNoHoverActions = [TableId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID];
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render cell actions with the content', () => {
+ const result = render(
+
+ {'test children'}
+
+ );
+ expect(result.queryByTestId('mockSecurityCellActions')).toBeInTheDocument();
+ expect(result.queryByText('test children')).toBeInTheDocument();
+ });
+
+ it('should call cell actions with the default props', () => {
+ render(
+
+ {'test children'}
+
+ );
+ expect(MockSecurityCellActions).toHaveBeenCalledWith(
+ expect.objectContaining({
+ data,
+ mode: 'hover-down',
+ sourcererScopeId: mockSourcererScopeId,
+ disabledActionTypes: [],
+ metadata: { scopeId: TimelineId.active },
+ })
+ );
+ });
+
+ describe('when value is empty', () => {
+ it('should set an empty array value to the SecurityCellActions component', () => {
+ render(
+
+ {'test children'}
+
+ );
+ expect(MockSecurityCellActions).toHaveBeenCalledWith(
+ expect.objectContaining({ data: { ...data, value: [] } })
+ );
+ });
+ });
+
+ describe('when queryValue is provided', () => {
+ it('should set value to queryValue', () => {
+ render(
+
+ {'test children'}
+
+ );
+ expect(MockSecurityCellActions).toHaveBeenCalledWith(
+ expect.objectContaining({ data: { ...data, value: 'testQueryValue' } })
+ );
+ });
+ });
+
+ describe('when scopeId is defined', () => {
+ it('should set the scopeId to the SecurityCellActions component metadata', () => {
+ render(
+
+ {'test children'}
+
+ );
+ expect(MockSecurityCellActions).toHaveBeenCalledWith(
+ expect.objectContaining({ metadata: { scopeId: 'testScopeId' } })
+ );
+ });
+ });
+
+ describe('when hideTopN is true', () => {
+ it('should set the disabledActionTypes to the SecurityCellActions component', () => {
+ render(
+
+ {'test children'}
+
+ );
+ expect(MockSecurityCellActions).toHaveBeenCalledWith(
+ expect.objectContaining({ disabledActionTypes: ['security-cellAction-type-showTopN'] })
+ );
+ });
+ });
+
+ scopeIdsWithHoverActions.forEach((scopeId) => {
+ test(`it renders hover actions (by default) when scopeId is '${scopeId}'`, async () => {
+ const { getByTestId } = render(
+
+ {'test children'}
+
+ );
+ expect(getByTestId('mockSecurityCellActions')).toBeInTheDocument();
+ });
+ });
+
+ scopeIdsNoHoverActions.forEach((scopeId) => {
+ test(`it does NOT render hover actions when scopeId is '${scopeId}'`, async () => {
+ const { queryByTestId } = render(
+
+ {'test children'}
+
+ );
+ expect(queryByTestId('mockSecurityCellActions')).not.toBeInTheDocument();
+ });
+ });
+
+ describe('text truncation styling', () => {
+ test('it applies text truncation styling when truncate IS specified', () => {
+ const { getByTestId } = render(
+
+
+ {'test children'}
+
+
+ );
+
+ expect(getByTestId('render-truncatable-content')).toBeInTheDocument();
+ });
+
+ test('it does NOT apply text truncation styling when truncate is NOT specified', () => {
+ const { queryByTestId } = render(
+
+
+ {'test children'}
+
+
+ );
+
+ expect(queryByTestId('render-truncatable-content')).not.toBeInTheDocument();
+ });
+ });
+ });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/cell_actions_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/cell_actions_renderer.tsx
new file mode 100644
index 0000000000000..b953ad22dbead
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/cell_actions_renderer.tsx
@@ -0,0 +1,177 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ToolTipPositions } from '@elastic/eui';
+import { EuiToolTip } from '@elastic/eui';
+import React, { useContext, useMemo } from 'react';
+import styled from '@emotion/styled';
+import { TimelineId } from '../../../../common/types';
+import type { SecurityCellActionsData } from '.';
+import {
+ CellActionsMode,
+ SecurityCellActions,
+ SecurityCellActionsTrigger,
+ SecurityCellActionType,
+} from '.';
+import { getSourcererScopeId } from '../../../helpers';
+import { TimelineContext } from '../../../timelines/components/timeline/context';
+import { TruncatableText } from '../truncatable_text';
+import { TableContext } from '../events_viewer/shared';
+import {
+ disableHoverActions,
+ tooltipContentIsExplicitlyNull,
+ getDefaultWhenTooltipIsUnspecified,
+} from './utils';
+
+export const ProviderContentWrapper = styled.span`
+ > span.euiToolTipAnchor {
+ display: block; /* allow EuiTooltip content to be truncatable */
+ }
+
+ > span.euiToolTipAnchor.eui-textTruncate {
+ display: inline-block; /* do not override display when a tooltip is truncated via eui-textTruncate */
+ }
+`;
+
+export interface CellActionsRendererProps {
+ hideTopN?: boolean;
+ field: string;
+ value?: string | number | null;
+ queryValue?: string | null;
+ children?: React.ReactNode;
+ scopeId?: string;
+ tooltipContent?: React.ReactNode;
+ tooltipPosition?: ToolTipPositions;
+ truncate?: boolean;
+}
+
+/**
+ * Renders the content of the cell actions, wrapped in a tooltip
+ */
+export const Content = React.memo<{
+ children?: React.ReactNode;
+ field: string;
+ tooltipContent?: React.ReactNode;
+ tooltipPosition?: ToolTipPositions;
+ value?: string | number | null;
+}>(({ children, field, tooltipContent, tooltipPosition, value }) =>
+ !tooltipContentIsExplicitlyNull(tooltipContent) ? (
+
+ <>{children ? children : value}>
+
+ ) : (
+ <>{children ? children : value}>
+ )
+);
+
+Content.displayName = 'Content';
+
+/**
+ * Renders cell actions content (or an arbitrary visualization specified by `children`)
+ * that's only displayed when the specified value is non-`null`.
+ * @param field - the name of the field, e.g. `network.transport`
+ * @param value - value of the field e.g. `tcp`
+ * @param children - defaults to displaying `value`, this allows an arbitrary visualization to be displayed in lieu of the default behavior
+ * @param tooltipContent - defaults to displaying `field`, pass `null` to
+ * prevent a tooltip from being displayed, or pass arbitrary content
+ * @param tooltipPosition - defaults to eui's default tooltip position
+ * @param queryValue - defaults to `value`, this query overrides the `queryMatch.value` used by the `DataProvider` that represents the data
+ * @param hideTopN - defaults to `false`, when true, the option to aggregate this field will be hidden
+ * @param scopeId - the id of the scope, e.g. `timelineId` or `tableId`
+ * @param truncate - defaults to `false`, when true, the content will be truncated
+ */
+export const CellActionsRenderer = React.memo(
+ ({
+ hideTopN = false,
+ field,
+ value,
+ children,
+ scopeId = TimelineId.active,
+ tooltipContent,
+ tooltipPosition,
+ queryValue,
+ truncate = false,
+ }) => {
+ const { timelineId: timelineIdFind } = useContext(TimelineContext);
+ const { tableId: tableIdFind } = useContext(TableContext);
+
+ const sourcererScopeId = useMemo(
+ () => getSourcererScopeId(scopeId ?? timelineIdFind ?? tableIdFind),
+ [scopeId, tableIdFind, timelineIdFind]
+ );
+
+ const data = useMemo(() => {
+ if (queryValue) {
+ return { value: queryValue, field };
+ }
+ return { value: value || [], field };
+ }, [value, field, queryValue]);
+
+ const disabledActionTypes = useMemo(
+ () => (hideTopN ? [SecurityCellActionType.SHOW_TOP_N] : []),
+ [hideTopN]
+ );
+
+ const content = useMemo(
+ () => (
+
+ {truncate ? (
+
+
+ {children}
+
+
+ ) : (
+
+
+ {children}
+
+
+ )}
+
+ ),
+ [children, field, truncate, tooltipContent, tooltipPosition, value]
+ );
+
+ if (value == null) return null;
+
+ if (disableHoverActions(scopeId)) {
+ return <>{content}>;
+ }
+
+ return (
+
+ {content}
+
+ );
+ }
+);
+
+CellActionsRenderer.displayName = 'CellActionsRenderer';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/utils.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/utils.test.tsx
new file mode 100644
index 0000000000000..f18b974482e2f
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/utils.test.tsx
@@ -0,0 +1,92 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { TableId } from '@kbn/securitysolution-data-table';
+import { TimelineId } from '../../../../common/types';
+import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants';
+import {
+ disableHoverActions,
+ tooltipContentIsExplicitlyNull,
+ getDefaultWhenTooltipIsUnspecified,
+} from './utils';
+
+const scopeIdsWithHoverActions = [
+ undefined,
+ TimelineId.active,
+ TableId.alternateTest,
+ TimelineId.casePage,
+ TableId.alertsOnAlertsPage,
+ TableId.alertsOnRuleDetailsPage,
+ TableId.hostsPageEvents,
+ TableId.hostsPageSessions,
+ TableId.kubernetesPageSessions,
+ TableId.networkPageEvents,
+ TimelineId.test,
+ TableId.usersPageEvents,
+];
+
+const scopeIdsNoHoverActions = [TableId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID];
+
+describe('disableHoverActions', () => {
+ scopeIdsNoHoverActions.forEach((scopeId) =>
+ test(`it returns true when timelineId is ${scopeId}`, () => {
+ expect(disableHoverActions(scopeId)).toBe(true);
+ })
+ );
+
+ scopeIdsWithHoverActions.forEach((scopeId) =>
+ test(`it returns false when timelineId is ${scopeId}`, () => {
+ expect(disableHoverActions(scopeId)).toBe(false);
+ })
+ );
+});
+
+describe('tooltipContentIsExplicitlyNull', () => {
+ test('returns false if a string is provided for the tooltip', () => {
+ expect(tooltipContentIsExplicitlyNull('bob')).toBe(false);
+ });
+
+ test('returns false if the tooltip is undefined', () => {
+ expect(tooltipContentIsExplicitlyNull(undefined)).toBe(false);
+ });
+
+ test('returns false if the tooltip is a ReactNode', () => {
+ expect(tooltipContentIsExplicitlyNull({'be a good node'})).toBe(false);
+ });
+
+ test('returns true if the tooltip is null', () => {
+ expect(tooltipContentIsExplicitlyNull(null)).toBe(true);
+ });
+});
+
+describe('getDefaultWhenTooltipIsUnspecified', () => {
+ test('it returns the field (as as string) when the tooltipContent is undefined', () => {
+ expect(getDefaultWhenTooltipIsUnspecified({ field: 'source.bytes' })).toEqual('source.bytes');
+ });
+
+ test('it returns the field (as as string) when the tooltipContent is null', () => {
+ expect(
+ getDefaultWhenTooltipIsUnspecified({ field: 'source.bytes', tooltipContent: null })
+ ).toEqual('source.bytes');
+ });
+
+ test('it returns the tooltipContent when a string is provided as content', () => {
+ expect(
+ getDefaultWhenTooltipIsUnspecified({ field: 'source.bytes', tooltipContent: 'a string' })
+ ).toEqual('a string');
+ });
+
+ test('it returns the tooltipContent when an element is provided as content', () => {
+ expect(
+ getDefaultWhenTooltipIsUnspecified({
+ field: 'source.bytes',
+ tooltipContent: {'the universe'},
+ })
+ ).toEqual({'the universe'});
+ });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/utils.ts b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/utils.ts
new file mode 100644
index 0000000000000..a69a66ed07bd4
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/cell_actions/utils.ts
@@ -0,0 +1,40 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type React from 'react';
+import { TableId } from '@kbn/securitysolution-data-table';
+import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants';
+
+/**
+ * Determines if hover actions should be disabled for a given timeline ID
+ * @param timelineId - The timeline ID to check
+ * @returns true if hover actions should be disabled, false otherwise
+ */
+export const disableHoverActions = (timelineId: string | undefined): boolean =>
+ [TableId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID].includes(timelineId ?? '');
+
+/**
+ * Only returns true if the specified tooltipContent is exactly `null`.
+ * Example input / output:
+ * `bob -> false`
+ * `undefined -> false`
+ * `thing -> false`
+ * `null -> true`
+ */
+export const tooltipContentIsExplicitlyNull = (tooltipContent?: React.ReactNode): boolean =>
+ tooltipContent === null; // an explicit / exact null check
+
+/**
+ * Derives the tooltip content from the field name if no tooltip was specified
+ */
+export const getDefaultWhenTooltipIsUnspecified = ({
+ field,
+ tooltipContent,
+}: {
+ field: string;
+ tooltipContent?: React.ReactNode;
+}): React.ReactNode => (tooltipContent != null ? tooltipContent : field);
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/barchart.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/barchart.test.tsx
index 645d3cd01c075..7ef04360e6bba 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/barchart.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/barchart.test.tsx
@@ -10,8 +10,6 @@ import { BarSeries, Axis, ScaleType } from '@elastic/charts';
import type { RenderResult } from '@testing-library/react';
import { screen, render } from '@testing-library/react';
import React from 'react';
-
-import { escapeDataProviderId } from '../drag_and_drop/helpers';
import { TestProviders } from '../../mock';
import '../../mock/react_beautiful_dnd';
@@ -377,14 +375,10 @@ describe('BarChart with stackByField', () => {
expect(screen.getByTestId('draggable-legend')).toBeInTheDocument();
});
- test.each(data)('it renders the expected draggable legend text for datum $key', ({ key }) => {
- const dataProviderId = `draggableId.content.draggable-legend-item-uuid_v4()-${escapeDataProviderId(
- stackByField
- )}-${escapeDataProviderId(key)}`;
-
- expect(
- wrapper.container.querySelector(`div[data-provider-id="${dataProviderId}"]`)
- ).toHaveTextContent(key);
+ data.forEach((datum, idx) => {
+ test(`it renders the expected draggable legend text for datum ${datum.key}`, () => {
+ expect(wrapper.getByTestId(`legend-item-${idx}`)).toHaveTextContent(datum.key);
+ });
});
});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/barchart.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/barchart.tsx
index 42a98a0b743db..3d0ad7c9765dd 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/barchart.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/barchart.tsx
@@ -12,11 +12,9 @@ import type { SettingsProps } from '@elastic/charts';
import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts';
import { getOr, get, isNumber } from 'lodash/fp';
import deepmerge from 'deepmerge';
-import { v4 as uuidv4 } from 'uuid';
import styled from '@emotion/styled';
import deepEqual from 'fast-deep-equal';
-import { escapeDataProviderId } from '../drag_and_drop/helpers';
import { useTimeZone } from '../../lib/kibana';
import { useThrottledResizeObserver } from '../utils';
import { hasValueToDisplay } from '../../utils/validators';
@@ -193,9 +191,6 @@ export const BarChartComponent: React.FC = ({
barChart != null && stackByField != null
? barChart.map((d) => ({
color: d.color,
- dataProviderId: escapeDataProviderId(
- `draggable-legend-item-${uuidv4()}-${stackByField}-${d.key}`
- ),
scopeId,
field: stackByField,
value: d.key,
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/donutchart.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/donutchart.test.tsx
index e29b72fbc0004..ac7e6b7a1ecc0 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/donutchart.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/donutchart.test.tsx
@@ -14,8 +14,6 @@ import type { DonutChartProps } from './donutchart';
import { DonutChart } from './donutchart';
import { DraggableLegend } from './draggable_legend';
import { ChartLabel } from '../../../overview/components/detection_response/alerts_by_status/chart_label';
-import { escapeDataProviderId } from '../drag_and_drop/helpers';
-import { v4 as uuidv4 } from 'uuid';
jest.mock('@elastic/charts', () => {
const actual = jest.requireActual('@elastic/charts');
@@ -73,8 +71,7 @@ describe('DonutChart', () => {
totalCount: parsedMockAlertsData?.open?.total,
legendItems: (['critical', 'high', 'medium', 'low'] as Severity[]).map((d) => ({
color: testColors[d],
- dataProviderId: escapeDataProviderId(`draggable-legend-item-${uuidv4()}-${d}`),
- timelineId: undefined,
+ scopeId: undefined,
field: 'kibana.alert.severity',
value: d,
})),
@@ -133,30 +130,26 @@ describe('DonutChart', () => {
expect((DraggableLegend as unknown as jest.Mock).mock.calls[0][0].legendItems).toEqual([
{
color: '#EF6550',
- dataProviderId: 'draggable-legend-item-test-uuid-critical',
field: 'kibana.alert.severity',
- timelineId: undefined,
+ scopeId: undefined,
value: 'critical',
},
{
color: '#EE9266',
- dataProviderId: 'draggable-legend-item-test-uuid-high',
field: 'kibana.alert.severity',
- timelineId: undefined,
+ scopeId: undefined,
value: 'high',
},
{
color: '#F3B689',
- dataProviderId: 'draggable-legend-item-test-uuid-medium',
field: 'kibana.alert.severity',
- timelineId: undefined,
+ scopeId: undefined,
value: 'medium',
},
{
color: '#F8D9B2',
- dataProviderId: 'draggable-legend-item-test-uuid-low',
field: 'kibana.alert.severity',
- timelineId: undefined,
+ scopeId: undefined,
value: 'low',
},
]);
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx
index 02efc7784b271..496920ec33cb1 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx
@@ -27,31 +27,24 @@ jest.mock('@elastic/eui', () => {
const legendItems: LegendItem[] = [
{
color: '#1EA593',
- dataProviderId: 'draggable-legend-item-3207fda7-d008-402a-86a0-8ad632081bad-event_dataset-flow',
field: 'event.dataset',
value: 'flow',
scopeId: 'test',
},
{
color: '#2B70F7',
- dataProviderId:
- 'draggable-legend-item-83f6c824-811d-4ec8-b373-eba2b0de6398-event_dataset-suricata_eve',
field: 'event.dataset',
value: 'suricata.eve',
scopeId: 'test',
},
{
color: '#CE0060',
- dataProviderId:
- 'draggable-legend-item-ec57bb8f-82cd-4e07-bd38-1d11b3f0ee5f-event_dataset-traefik_access',
field: 'event.dataset',
value: 'traefik.access',
scopeId: 'test',
},
{
color: '#38007E',
- dataProviderId:
- 'draggable-legend-item-25d5fcd6-87ba-46b5-893e-c655d7d504e3-event_dataset-esensor',
field: 'event.dataset',
value: 'esensor',
scopeId: 'test',
@@ -104,16 +97,14 @@ describe('DraggableLegend', () => {
});
it('renders the legend items', () => {
- const { container } = render(
+ const { getByTestId } = render(
);
- legendItems.forEach((item) =>
- expect(
- container.querySelector(`[data-provider-id="draggableId.content.${item.dataProviderId}"]`)
- ).toHaveTextContent(item.value.toString())
+ legendItems.forEach((item, idx) =>
+ expect(getByTestId(`legend-item-${idx}`)).toHaveTextContent(item.value.toString())
);
});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend.tsx
index b1716773a0e3b..75ab9a406c370 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend.tsx
@@ -78,8 +78,8 @@ const DraggableLegendComponent: React.FC<{
>
- {legendItems.map((item) => (
-
+ {legendItems.map((item, idx) => (
+
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx
index a799d467eb23f..f8e5c662e89ca 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx
@@ -35,7 +35,6 @@ jest.mock('../cell_actions', () => ({
describe('DraggableLegendItem', () => {
const legendItem: LegendItem = {
color: '#1EA593',
- dataProviderId: 'draggable-legend-item-3207fda7-d008-402a-86a0-8ad632081bad-event_dataset-flow',
field: 'event.dataset',
value: 'flow',
scopeId: 'test',
@@ -74,16 +73,13 @@ describe('DraggableLegendItem', () => {
});
it('renders draggable legend item text', () => {
- const { container } = render(
+ const { getByTestId } = render(
);
- const legendItemElement = container.querySelector(
- `[data-provider-id="draggableId.content.${legendItem.dataProviderId}"]`
- );
- expect(legendItemElement).toHaveTextContent(String(legendItem.value));
+ expect(getByTestId('legend-item')).toHaveTextContent('flow');
});
it('renders a custom legend item via the `render` prop when provided', () => {
@@ -122,12 +118,7 @@ describe('DraggableLegendItem', () => {
);
- expect(screen.getByTestId('mockSecurityCellActions')).toBeInTheDocument();
- expect(MockSecurityCellActions).toHaveBeenCalledWith(
- expect.objectContaining({
- disabledActionTypes: ['security-cellAction-type-showTopN'],
- })
- );
+ expect(screen.getByTestId('legend-item')).toBeInTheDocument();
});
it('renders the empty value label when the value is empty', () => {
@@ -156,7 +147,7 @@ describe('DraggableLegendItem', () => {
);
- expect(queryByTestId(`legend-item-${legendItem.dataProviderId}`)).not.toBeInTheDocument();
+ expect(queryByTestId(`render-content-${legendItem.field}`)).not.toBeInTheDocument();
expect(getByTestId('legendItemInlineActions')).toBeInTheDocument();
});
});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx
index 812f7be182926..4ccd7ec31e6ad 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx
@@ -11,7 +11,7 @@ import React, { useMemo } from 'react';
import styled from '@emotion/styled';
import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants';
-import { DefaultDraggable } from '../draggables';
+import { CellActionsRenderer } from '../cell_actions/cell_actions_renderer';
import { useUiSetting$ } from '../../lib/kibana';
import { EMPTY_VALUE_LABEL } from './translation';
import { hasValueToDisplay } from '../../utils/validators';
@@ -29,7 +29,6 @@ const CountFlexItem = styled(EuiFlexItem)`
export interface LegendItem {
color?: string;
- dataProviderId: string;
render?: (fieldValuePair?: { field: string; value: string | number }) => React.ReactNode;
field: string;
scopeId?: string;
@@ -55,7 +54,7 @@ const DraggableLegendItemComponent: React.FC<{
isInlineActions?: boolean;
}> = ({ legendItem, isInlineActions = false }) => {
const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT);
- const { color, count, dataProviderId, field, scopeId, value } = legendItem;
+ const { color, count, field, scopeId, value } = legendItem;
const sourcererScopeId = getSourcererScopeId(scopeId ?? '');
const content = useMemo(() => {
@@ -82,19 +81,13 @@ const DraggableLegendItemComponent: React.FC<{
gutterSize="none"
responsive={false}
>
-
+
{isInlineActions ? (
content
) : (
-
+
{content}
-
+
)}
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/draggable_wrapper.test.tsx.snap b/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/draggable_wrapper.test.tsx.snap
deleted file mode 100644
index aa8214938c2b0..0000000000000
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/__snapshots__/draggable_wrapper.test.tsx.snap
+++ /dev/null
@@ -1,22 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`DraggableWrapper rendering it renders against the snapshot 1`] = `
-
-`;
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/cell_actions_wrapper.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/cell_actions_wrapper.test.tsx
deleted file mode 100644
index 8f438bb9289c4..0000000000000
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/cell_actions_wrapper.test.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React from 'react';
-import { render } from '@testing-library/react';
-import { CellActionsWrapper } from './cell_actions_wrapper';
-import { CellActionsMode, SecurityCellActionType } from '../cell_actions';
-import { TimelineId, type DataProvider } from '../../../../common/types';
-
-const MockSecurityCellActions = jest.fn(({ children }: { children: React.ReactNode }) => (
- {children}
-));
-jest.mock('../cell_actions', () => ({
- ...jest.requireActual('../cell_actions'),
- SecurityCellActions: (params: { children: React.ReactNode }) => MockSecurityCellActions(params),
-}));
-
-const mockSourcererScopeId = 'testSourcererScopeId';
-jest.mock('../../../helpers', () => ({
- getSourcererScopeId: jest.fn(() => mockSourcererScopeId),
-}));
-
-const dataProvider = { queryMatch: { field: 'host.name', value: '12345' } } as DataProvider;
-const data = { ...dataProvider.queryMatch };
-
-describe('CellActionsWrapper', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it('should render cell actions with the content', () => {
- const result = render(
- {'test children'}
- );
- expect(result.queryByTestId('mockSecurityCellActions')).toBeInTheDocument();
- expect(result.queryByText('test children')).toBeInTheDocument();
- });
-
- it('should call cell actions with the default props', () => {
- render({'test children'});
- expect(MockSecurityCellActions).toHaveBeenCalledWith(
- expect.objectContaining({
- data,
- mode: CellActionsMode.HOVER_DOWN,
- sourcererScopeId: mockSourcererScopeId,
- disabledActionTypes: [],
- metadata: { scopeId: TimelineId.active },
- })
- );
- });
-
- describe('when dataProvider value is empty', () => {
- it('should set an empty array value to the SecurityCellActions component', () => {
- const emptyValueDataProvider = {
- queryMatch: { field: 'host.name', value: '' },
- } as DataProvider;
- render(
-
- {'test children'}
-
- );
- expect(MockSecurityCellActions).toHaveBeenCalledWith(
- expect.objectContaining({ data: { ...data, value: [] } })
- );
- });
- });
-
- describe('when scopeId is defined', () => {
- it('should set the scopeId to the SecurityCellActions component metadata', () => {
- render(
-
- {'test children'}
-
- );
- expect(MockSecurityCellActions).toHaveBeenCalledWith(
- expect.objectContaining({ metadata: { scopeId: 'testScopeId' } })
- );
- });
- });
-
- describe('when hideTopN is true', () => {
- it('should set the disabledActionTypes to the SecurityCellActions component', () => {
- render(
-
- {'test children'}
-
- );
- expect(MockSecurityCellActions).toHaveBeenCalledWith(
- expect.objectContaining({ disabledActionTypes: [SecurityCellActionType.SHOW_TOP_N] })
- );
- });
- });
-});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/cell_actions_wrapper.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/cell_actions_wrapper.tsx
deleted file mode 100644
index 7d20a645abc35..0000000000000
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/cell_actions_wrapper.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { PropsWithChildren } from 'react';
-import React, { useContext, useMemo } from 'react';
-import { TimelineId, type DataProvider } from '../../../../common/types';
-import type { SecurityCellActionsData } from '../cell_actions';
-import {
- CellActionsMode,
- SecurityCellActions,
- SecurityCellActionsTrigger,
- SecurityCellActionType,
-} from '../cell_actions';
-import { getSourcererScopeId } from '../../../helpers';
-import { TimelineContext } from '../../../timelines/components/timeline/context';
-
-import { TableContext } from '../events_viewer/shared';
-
-type CellActionsWrapperProps = PropsWithChildren<{
- /**
- * The `dataProvider` is used to retrieve the `field` and `value` for the cell actions execution.
- * */
- dataProvider: DataProvider;
- /**
- * The `hideTopN` is used hide the show top N action. Defaults to `false`.
- * */
- hideTopN?: boolean;
- /**
- * The `scopeId` is used to determine the context of the cell actions execution.
- * If not provided this component will try to retrieve the timeline id or the table id from the context, in that order.
- * */
- scopeId?: string;
-}>;
-
-export const CellActionsWrapper = React.memo>(
- ({ dataProvider, scopeId = TimelineId.active, children, hideTopN = false }) => {
- const { timelineId: timelineIdFind } = useContext(TimelineContext);
- const { tableId: tableIdFind } = useContext(TableContext);
-
- const sourcererScopeId = useMemo(
- () => getSourcererScopeId(scopeId ?? timelineIdFind ?? tableIdFind),
- [scopeId, tableIdFind, timelineIdFind]
- );
-
- const data = useMemo(() => {
- const { value, field } = dataProvider.queryMatch;
- return { value: value || [], field };
- }, [dataProvider.queryMatch]);
-
- const disabledActionTypes = useMemo(
- () => (hideTopN ? [SecurityCellActionType.SHOW_TOP_N] : []),
- [hideTopN]
- );
-
- return (
-
- {children}
-
- );
- }
-);
-
-CellActionsWrapper.displayName = 'CellActionsWrapper';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx
deleted file mode 100644
index 6cc769debbfa9..0000000000000
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { render } from '@testing-library/react';
-import { shallow } from 'enzyme';
-import React from 'react';
-import type { DraggableStateSnapshot, DraggingStyle } from '@hello-pangea/dnd';
-
-import { mockBrowserFields } from '../../containers/source/mock';
-import { TestProviders } from '../../mock';
-import { mockDataProviders } from '../../../timelines/components/timeline/data_providers/mock/mock_data_providers';
-import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants';
-import { DragDropContextWrapper } from './drag_drop_context_wrapper';
-import { disableHoverActions, DraggableWrapper, getStyle } from './draggable_wrapper';
-import { useMountAppended } from '../../utils/use_mount_appended';
-import { TimelineId } from '../../../../common/types';
-import { TableId } from '@kbn/securitysolution-data-table';
-
-jest.mock('../../lib/kibana');
-
-jest.mock('@elastic/eui', () => {
- const original = jest.requireActual('@elastic/eui');
- return {
- ...original,
- EuiScreenReaderOnly: () => <>>,
- };
-});
-
-const MockSecurityCellActions = jest.fn(({ children }: { children: React.ReactNode }) => (
- {children}
-));
-jest.mock('../cell_actions', () => ({
- ...jest.requireActual('../cell_actions'),
- SecurityCellActions: (props: { children: React.ReactNode }) => MockSecurityCellActions(props),
-}));
-
-const scopeIdsWithHoverActions = [
- undefined,
- TimelineId.active,
- TableId.alternateTest,
- TimelineId.casePage,
- TableId.alertsOnAlertsPage,
- TableId.alertsOnRuleDetailsPage,
- TableId.hostsPageEvents,
- TableId.hostsPageSessions,
- TableId.kubernetesPageSessions,
- TableId.networkPageEvents,
- TimelineId.test,
- TableId.usersPageEvents,
-];
-
-const scopeIdsNoHoverActions = [TableId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID];
-
-describe('DraggableWrapper', () => {
- const dataProvider = mockDataProviders[0];
- const message = 'draggable wrapper content';
- const mount = useMountAppended();
-
- beforeEach(() => {
- jest.useFakeTimers({ legacyFakeTimers: true });
- });
-
- afterEach(() => {
- const portal = document.querySelector('[data-euiportal="true"]');
- if (portal != null) {
- portal.innerHTML = '';
- }
- });
-
- afterAll(() => {
- jest.useRealTimers();
- });
-
- describe('rendering', () => {
- test('it renders against the snapshot', () => {
- const wrapper = shallow(
-
-
- message} />
-
-
- );
-
- expect(wrapper.find('DraggableWrapper')).toMatchSnapshot();
- });
-
- test('it renders the children passed to the render prop', () => {
- const wrapper = mount(
-
-
- message} />
-
-
- );
-
- expect(wrapper.text()).toEqual(message);
- });
-
- scopeIdsWithHoverActions.forEach((scopeId) => {
- test(`it renders hover actions (by default) when 'isDraggable' is false and timelineId is '${scopeId}'`, async () => {
- const { queryByTestId } = render(
-
-
- message}
- scopeId={scopeId}
- />
-
-
- );
-
- expect(queryByTestId('cell-actions-mock')).toBeInTheDocument();
- });
- });
-
- scopeIdsNoHoverActions.forEach((scopeId) => {
- test(`it does NOT render hover actions when 'isDraggable' is false and timelineId is '${scopeId}'`, async () => {
- const { queryByTestId } = render(
-
-
- message}
- scopeId={scopeId}
- />
-
-
- );
-
- expect(queryByTestId('cell-actions-mock')).not.toBeInTheDocument();
- });
- });
- });
-
- describe('text truncation styling', () => {
- test('it applies text truncation styling when truncate IS specified (implicit: and the user is not dragging)', () => {
- const wrapper = mount(
-
-
- message} truncate />
-
-
- );
-
- expect(wrapper.find('[data-test-subj="render-truncatable-content"]').exists()).toEqual(true);
- });
-
- test('it does NOT apply text truncation styling when truncate is NOT specified', () => {
- const wrapper = mount(
-
-
- message} />
-
-
- );
-
- expect(wrapper.find('[data-test-subj="draggable-truncatable-content"]').exists()).toEqual(
- false
- );
- });
- });
-});
-
-describe('ConditionalPortal', () => {
- describe('getStyle', () => {
- const style: DraggingStyle = {
- boxSizing: 'border-box',
- height: 10,
- left: 1,
- pointerEvents: 'none',
- position: 'fixed',
- transition: 'none',
- top: 123,
- width: 50,
- zIndex: 9999,
- };
-
- it('returns a style with no transitionDuration when the snapshot is not drop animating', () => {
- const snapshot: DraggableStateSnapshot = {
- isDragging: true,
- isDropAnimating: false, // <-- NOT drop animating
- isClone: false,
- dropAnimation: null,
- draggingOver: null,
- combineWith: null,
- combineTargetFor: null,
- mode: null,
- };
-
- expect(getStyle(style, snapshot)).not.toHaveProperty('transitionDuration');
- });
-
- it('returns a style with a transitionDuration when the snapshot is drop animating', () => {
- const snapshot: DraggableStateSnapshot = {
- isDragging: true,
- isDropAnimating: true, // <-- it is drop animating
- isClone: false,
- dropAnimation: null,
- draggingOver: null,
- combineWith: null,
- combineTargetFor: null,
- mode: null,
- };
-
- expect(getStyle(style, snapshot)).toHaveProperty('transitionDuration', '0.00000001s');
- });
- });
-
- describe('disableHoverActions', () => {
- scopeIdsNoHoverActions.forEach((scopeId) =>
- test(`it returns true when timelineId is ${scopeId}`, () => {
- expect(disableHoverActions(scopeId)).toBe(true);
- })
- );
-
- scopeIdsWithHoverActions.forEach((scopeId) =>
- test(`it returns false when timelineId is ${scopeId}`, () => {
- expect(disableHoverActions(scopeId)).toBe(false);
- })
- );
- });
-});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx
deleted file mode 100644
index 04db0f329c33c..0000000000000
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React, { useMemo } from 'react';
-import type {
- DraggableProvided,
- DraggableStateSnapshot,
- DraggingStyle,
- NotDraggingStyle,
-} from '@hello-pangea/dnd';
-import styled from 'styled-components';
-import { TableId } from '@kbn/securitysolution-data-table';
-import type { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider';
-import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants';
-import { TruncatableText } from '../truncatable_text';
-import { CellActionsWrapper } from './cell_actions_wrapper';
-import { getDraggableId } from './helpers';
-
-export const ProviderContentWrapper = styled.span`
- > span.euiToolTipAnchor {
- display: block; /* allow EuiTooltip content to be truncatable */
- }
-
- > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block; /* do not override display when a tooltip is truncated via eui-textTruncate */
- }
-`;
-
-export type RenderFunctionProp = (
- props: DataProvider,
- provided: DraggableProvided | null,
- state: DraggableStateSnapshot
-) => React.ReactNode;
-
-export interface DraggableWrapperProps {
- dataProvider: DataProvider;
- fieldType?: string;
- isAggregatable?: boolean;
- hideTopN?: boolean;
- render: RenderFunctionProp;
- scopeId?: string;
- truncate?: boolean;
-}
-
-export const disableHoverActions = (timelineId: string | undefined): boolean =>
- [TableId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID].includes(timelineId ?? '');
-
-/**
- * Wraps a draggable component to handle registration / unregistration of the
- * data provider associated with the item being dropped
- */
-
-export const getStyle = (
- style: DraggingStyle | NotDraggingStyle | undefined,
- snapshot: DraggableStateSnapshot
-) => {
- if (!snapshot.isDropAnimating) {
- return style;
- }
-
- return {
- ...style,
- transitionDuration: '0.00000001s', // cannot be 0, but can be a very short duration
- };
-};
-
-export const DraggableWrapper: React.FC = React.memo(
- ({ dataProvider, render, scopeId, truncate, hideTopN }) => {
- const content = useMemo(
- () => (
-
- {truncate ? (
-
- {render(dataProvider, null, {
- isDragging: false,
- isDropAnimating: false,
- isClone: false,
- dropAnimation: null,
- draggingOver: null,
- combineWith: null,
- combineTargetFor: null,
- mode: null,
- })}
-
- ) : (
-
- {render(dataProvider, null, {
- isDragging: false,
- isDropAnimating: false,
- isClone: false,
- dropAnimation: null,
- draggingOver: null,
- combineWith: null,
- combineTargetFor: null,
- mode: null,
- })}
-
- )}
-
- ),
- [dataProvider, render, truncate]
- );
-
- if (disableHoverActions(scopeId)) {
- return <>{content}>;
- }
- return (
-
- {content}
-
- );
- }
-);
-DraggableWrapper.displayName = 'DraggableWrapper';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap b/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap
index 33639b851c324..782be3a0cdd09 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/__snapshots__/index.test.tsx.snap
@@ -1,9 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`draggables rendering it renders the default Badge 1`] = `
-
@@ -16,29 +15,5 @@ exports[`draggables rendering it renders the default Badge 1`] = `
A child of this
-
-`;
-
-exports[`draggables rendering it renders the default DefaultDraggable 1`] = `
-
+
`;
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/index.test.tsx
index 2613714d5d9d1..765ffce045e40 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/index.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/index.test.tsx
@@ -12,12 +12,7 @@ import { TestProviders } from '../../mock';
import { getEmptyString } from '../empty_value';
import { useMountAppended } from '../../utils/use_mount_appended';
-import {
- DefaultDraggable,
- DraggableBadge,
- getDefaultWhenTooltipIsUnspecified,
- tooltipContentIsExplicitlyNull,
-} from '.';
+import { DraggableBadge } from '.';
jest.mock('../../lib/kibana');
@@ -25,20 +20,6 @@ describe('draggables', () => {
const mount = useMountAppended();
describe('rendering', () => {
- test('it renders the default DefaultDraggable', () => {
- const wrapper = shallow(
-
- {'A child of this'}
-
- );
- expect(wrapper).toMatchSnapshot();
- });
-
test('it renders the default Badge', () => {
const wrapper = shallow(
{
});
});
- describe('#tooltipContentIsExplicitlyNull', () => {
- test('returns false if a string is provided for the tooltip', () => {
- expect(tooltipContentIsExplicitlyNull('bob')).toBe(false);
- });
-
- test('returns false if the tooltip is undefined', () => {
- expect(tooltipContentIsExplicitlyNull(undefined)).toBe(false);
- });
-
- test('returns false if the tooltip is a ReactNode', () => {
- expect(tooltipContentIsExplicitlyNull({'be a good node'})).toBe(false);
- });
-
- test('returns true if the tooltip is null', () => {
- expect(tooltipContentIsExplicitlyNull(null)).toBe(true);
- });
- });
-
- describe('#getDefaultWhenTooltipIsUnspecified', () => {
- test('it returns the field (as as string) when the tooltipContent is undefined', () => {
- expect(getDefaultWhenTooltipIsUnspecified({ field: 'source.bytes' })).toEqual('source.bytes');
- });
-
- test('it returns the field (as as string) when the tooltipContent is null', () => {
- expect(
- getDefaultWhenTooltipIsUnspecified({ field: 'source.bytes', tooltipContent: null })
- ).toEqual('source.bytes');
- });
-
- test('it returns the tooltipContent when a string is provided as content', () => {
- expect(
- getDefaultWhenTooltipIsUnspecified({ field: 'source.bytes', tooltipContent: 'a string' })
- ).toEqual('a string');
- });
-
- test('it returns the tooltipContent when an element is provided as content', () => {
- expect(
- getDefaultWhenTooltipIsUnspecified({
- field: 'source.bytes',
- tooltipContent: {'the universe'},
- })
- ).toEqual({'the universe'});
- });
- });
-
- describe('DefaultDraggable', () => {
- test('it works with just an id, field, and value and is some value', () => {
- const field = 'some-field';
- const value = 'some value';
- const wrapper = mount(
-
-
-
- );
- expect(wrapper.text()).toEqual(value);
- });
-
- test('it returns null if value is undefined', () => {
- const wrapper = shallow(
-
- );
- expect(wrapper.isEmptyRender()).toBeTruthy();
- });
-
- test('it returns null if value is null', () => {
- const wrapper = shallow(
-
- );
- expect(wrapper.isEmptyRender()).toBeTruthy();
- });
-
- test('it renders a tooltip with the field name if a tooltip is not explicitly provided', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(
- wrapper.find('[data-test-subj="source.bytes-tooltip"]').first().props().content
- ).toEqual('source.bytes');
- });
-
- test('it renders the tooltipContent when a string is provided as content', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(
- wrapper.find('[data-test-subj="source.bytes-tooltip"]').first().props().content
- ).toEqual('default draggable string tooltip');
- });
-
- test('it renders the tooltipContent when an element is provided as content', () => {
- const wrapper = mount(
-
- {'default draggable tooltip'}}
- value="a default draggable"
- />
-
- );
-
- expect(
- wrapper.find('[data-test-subj="source.bytes-tooltip"]').first().props().content
- ).toEqual({'default draggable tooltip'});
- });
-
- test('it does NOT render a tooltip when tooltipContent is null', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find('[data-test-subj="source.bytes-tooltip"]').first().exists()).toBe(false);
- });
- });
-
describe('DraggableBadge', () => {
test('it works with just an id, field, and value and is the default', () => {
const wrapper = mount(
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/index.tsx
index 625fbff36568e..8934e01382cdc 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/index.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/draggables/index.tsx
@@ -5,160 +5,13 @@
* 2.0.
*/
-import type { IconType, ToolTipPositions } from '@elastic/eui';
-import { EuiBadge, EuiToolTip } from '@elastic/eui';
-import React, { useCallback, useMemo } from 'react';
+import type { IconType } from '@elastic/eui';
+import { EuiBadge } from '@elastic/eui';
+import React from 'react';
import styled from '@emotion/styled';
-
-import type { DraggableWrapperProps } from '../drag_and_drop/draggable_wrapper';
-import { DraggableWrapper } from '../drag_and_drop/draggable_wrapper';
-import { escapeDataProviderId } from '../drag_and_drop/helpers';
+import type { CellActionsRendererProps } from '../cell_actions/cell_actions_renderer';
+import { CellActionsRenderer } from '../cell_actions/cell_actions_renderer';
import { getEmptyStringTag } from '../empty_value';
-import type { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider';
-import { IS_OPERATOR } from '../../../timelines/components/timeline/data_providers/data_provider';
-
-export interface DefaultDraggableType {
- hideTopN?: boolean;
- id: string;
- fieldType?: string;
- isAggregatable?: boolean;
- field: string;
- value?: string | number | null;
- name?: string | null;
- queryValue?: string | null;
- children?: React.ReactNode;
- scopeId?: string;
- tooltipContent?: React.ReactNode;
- tooltipPosition?: ToolTipPositions;
- truncate?: boolean;
-}
-
-/**
- * Only returns true if the specified tooltipContent is exactly `null`.
- * Example input / output:
- * `bob -> false`
- * `undefined -> false`
- * `thing -> false`
- * `null -> true`
- */
-export const tooltipContentIsExplicitlyNull = (tooltipContent?: React.ReactNode): boolean =>
- tooltipContent === null; // an explicit / exact null check
-
-/**
- * Derives the tooltip content from the field name if no tooltip was specified
- */
-export const getDefaultWhenTooltipIsUnspecified = ({
- field,
- tooltipContent,
-}: {
- field: string;
- tooltipContent?: React.ReactNode;
-}): React.ReactNode => (tooltipContent != null ? tooltipContent : field);
-
-/**
- * Renders the content of the draggable, wrapped in a tooltip
- */
-export const Content = React.memo<{
- children?: React.ReactNode;
- field: string;
- tooltipContent?: React.ReactNode;
- tooltipPosition?: ToolTipPositions;
- value?: string | number | null;
-}>(({ children, field, tooltipContent, tooltipPosition, value }) =>
- !tooltipContentIsExplicitlyNull(tooltipContent) ? (
-
- <>{children ? children : value}>
-
- ) : (
- <>{children ? children : value}>
- )
-);
-
-Content.displayName = 'Content';
-
-/**
- * Draggable text (or an arbitrary visualization specified by `children`)
- * that's only displayed when the specified value is non-`null`.
- *
- * @param id - a unique draggable id, which typically follows the format `${contextId}-${eventId}-${field}-${value}`
- * @param field - the name of the field, e.g. `network.transport`
- * @param value - value of the field e.g. `tcp`
- * @param name - defaulting to `field`, this optional human readable name is used by the `DataProvider` that represents the data
- * @param children - defaults to displaying `value`, this allows an arbitrary visualization to be displayed in lieu of the default behavior
- * @param tooltipContent - defaults to displaying `field`, pass `null` to
- * prevent a tooltip from being displayed, or pass arbitrary content
- * @param tooltipPosition - defaults to eui's default tooltip position
- * @param queryValue - defaults to `value`, this query overrides the `queryMatch.value` used by the `DataProvider` that represents the data
- * @param hideTopN - defaults to `false`, when true, the option to aggregate this field will be hidden
- */
-export const DefaultDraggable = React.memo(
- ({
- hideTopN = false,
- id,
- field,
- fieldType = '',
- isAggregatable = false,
- value,
- name,
- children,
- scopeId,
- tooltipContent,
- tooltipPosition,
- queryValue,
- truncate,
- }) => {
- const dataProviderProp: DataProvider = useMemo(
- () => ({
- and: [],
- enabled: true,
- id: escapeDataProviderId(id),
- name: name ? name : value?.toString() ?? '',
- excluded: false,
- kqlQuery: '',
- queryMatch: {
- field,
- value: queryValue ? queryValue : value ?? '',
- operator: IS_OPERATOR,
- },
- }),
- [field, id, name, queryValue, value]
- );
-
- const renderCallback = useCallback(
- () => (
-
- {children}
-
- ),
- [children, field, tooltipContent, tooltipPosition, value]
- );
-
- if (value == null) return null;
-
- return (
-
- );
- }
-);
-
-DefaultDraggable.displayName = 'DefaultDraggable';
export const Badge = styled(EuiBadge)`
vertical-align: top;
@@ -166,17 +19,21 @@ export const Badge = styled(EuiBadge)`
Badge.displayName = 'Badge';
-export type BadgeDraggableType = Omit & {
- contextId: string;
- eventId: string;
+export type BadgeType = CellActionsRendererProps & {
iconType?: IconType;
+ // Teporary workaround accept these unused props
+ // TODO: remove these props in all DraggableBadge components
+ id?: string;
+ contextId?: string;
+ eventId?: string;
+ isAggregatable?: boolean;
+ fieldType?: string;
+ name?: string;
};
/**
* A draggable badge that's only displayed when the specified value is non-`null`.
*
- * @param contextId - used as part of the formula to derive a unique draggable id, this describes the context e.g. `event-fields-browser` in which the badge is displayed
- * @param eventId - uniquely identifies an event, as specified in the `_id` field of the document
* @param field - the name of the field, e.g. `network.transport`
* @param value - value of the field e.g. `tcp`
* @param iconType -the (optional) type of icon e.g. `snowflake` to display on the badge
@@ -186,27 +43,18 @@ export type BadgeDraggableType = Omit & {
* prevent a tooltip from being displayed, or pass arbitrary content
* @param queryValue - defaults to `value`, this query overrides the `queryMatch.value` used by the `DataProvider` that represents the data
*/
-const DraggableBadgeComponent: React.FC = ({
- contextId,
- eventId,
+const DraggableBadgeComponent: React.FC = ({
field,
value,
iconType,
- isAggregatable,
- fieldType,
- name,
children,
scopeId,
tooltipContent,
queryValue,
}) =>
value != null ? (
- = ({
{children ? children : value !== '' ? value : getEmptyStringTag()}
-
+
) : null;
DraggableBadgeComponent.displayName = 'DraggableBadgeComponent';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.test.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.test.ts
index d7f0276afab6f..a6b4cf1896bbc 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.test.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.test.ts
@@ -75,18 +75,6 @@ describe('legend', () => {
).toEqual(34);
});
- it('returns the expected dataProviderId', () => {
- expect(
- getLegendItemFromRawBucket({
- bucket: bucketsWithStackByField1[0],
- colorPalette,
- maxRiskSubAggregations,
- showColor: true,
- stackByField0: 'kibana.alert.rule.name',
- }).dataProviderId
- ).toContain('draggable-legend-item-treemap-kibana_alert_rule_name-matches everything-');
- });
-
it('renders the expected label', () => {
const item = getLegendItemFromRawBucket({
bucket: bucketsWithStackByField1[0],
@@ -189,20 +177,6 @@ describe('legend', () => {
expect(legendItem.render != null && legendItem.render()).toEqual('Host-k8iyfzraq9');
});
-
- it('returns the expected dataProviderId', () => {
- const legendItem = getLegendItemFromFlattenedBucket({
- colorPalette,
- flattenedBucket,
- maxRiskSubAggregations,
- stackByField0: 'kibana.alert.rule.name',
- stackByField1: 'host.name',
- });
-
- expect(legendItem.dataProviderId).toContain(
- 'draggable-legend-item-treemap-matches everything-Host-k8iyfzraq9-'
- );
- });
});
describe('getFirstGroupLegendItems', () => {
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.ts
index b35fb012a7eb1..79bfa4520fb69 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.ts
@@ -5,13 +5,10 @@
* 2.0.
*/
-import { v4 as uuidv4 } from 'uuid';
-
import { TableId } from '@kbn/securitysolution-data-table';
import { firstNonNullValue } from '../../../../../../../../common/endpoint/models/ecs_safety_helpers';
import type { LegendItem } from '../../../../../../../common/components/charts/draggable_legend_item';
import { getFillColor } from '../chart_palette';
-import { escapeDataProviderId } from '../../../../../../../common/components/drag_and_drop/helpers';
import { getLabel } from '../labels';
import type { FlattenedBucket, RawBucket } from '../../types';
@@ -35,9 +32,6 @@ export const getLegendItemFromRawBucket = ({
})
: undefined,
count: bucket.doc_count,
- dataProviderId: escapeDataProviderId(
- `draggable-legend-item-treemap-${stackByField0}-${bucket.key}-${uuidv4()}`
- ),
render: () =>
getLabel({
baseLabel: bucket.key_as_string ?? firstNonNullValue(bucket.key) ?? '', // prefer key_as_string when available, because it contains a formatted date
@@ -66,9 +60,6 @@ export const getLegendItemFromFlattenedBucket = ({
colorPalette,
}),
count: stackByField1DocCount,
- dataProviderId: escapeDataProviderId(
- `draggable-legend-item-treemap-${key}-${stackByField1Key}-${uuidv4()}`
- ),
render: () => `${stackByField1Key}`,
field: `${stackByField1}`,
value: `${stackByField1Key}`,
diff --git a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/details/__snapshots__/index.test.tsx.snap b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/details/__snapshots__/index.test.tsx.snap
index 5ad1e9553d4bf..d0f3c156f78ba 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/details/__snapshots__/index.test.tsx.snap
+++ b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/details/__snapshots__/index.test.tsx.snap
@@ -34,14 +34,6 @@ exports[`IP Overview Component rendering it renders the default IP Overview 1`]
opacity: 1;
}
-.c1 > span.euiToolTipAnchor {
- display: block;
-}
-
-.c1 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
span.euiToolTipAnchor {
- display: block;
-}
-
-.c1 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
(
description: locationRenderer(
[`${flowTarget}.geo.city_name`, `${flowTarget}.geo.region_name`],
data,
- contextID
+ scopeId
),
},
{
title: i18n.AUTONOMOUS_SYSTEM,
description: typeData
- ? autonomousSystemRenderer(typeData.autonomousSystem, flowTarget, contextID)
+ ? autonomousSystemRenderer(typeData.autonomousSystem, flowTarget, scopeId)
: getEmptyTagValue(),
},
];
@@ -165,7 +165,6 @@ export const IpOverview = React.memo(
scopeId,
host: data.host,
ipFilter: ip,
- contextID,
isFlyoutOpen,
})
: getEmptyTagValue(),
diff --git a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap
index 741e186b28212..31f47946b10e6 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap
+++ b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap
@@ -1,14 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PointToolTipContent renders correctly against snapshot 1`] = `
-.c0 > span.euiToolTipAnchor {
- display: block;
-}
-
-.c0 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
- .c0 > span.euiToolTipAnchor {
- display: block;
-}
-
-.c0 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
-
- .c0 > span.euiToolTipAnchor {
- display: block;
-}
-
-.c0 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
-
@@ -113,11 +95,10 @@ exports[`Field Renderers #hostIdRenderer it renders correctly against snapshot 1
>
- .c0 > span.euiToolTipAnchor {
- display: block;
-}
-
-.c0 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
-
@@ -170,11 +143,10 @@ exports[`Field Renderers #hostNameRenderer it renders correctly against snapshot
>
- .c0 > span.euiToolTipAnchor {
- display: block;
-}
-
-.c0 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
-
{
describe('#locationRenderer', () => {
test('it renders correctly against snapshot', () => {
const { asFragment } = render(
- locationRenderer(['source.geo.city_name', 'source.geo.region_name'], mockData.complete),
+ locationRenderer(
+ ['source.geo.city_name', 'source.geo.region_name'],
+ mockData.complete,
+ scopeId
+ ),
{ wrapper: TestProviders }
);
@@ -51,14 +55,14 @@ describe('Field Renderers', () => {
});
test('it renders emptyTagValue when no fields provided', () => {
- render({locationRenderer([], mockData.complete)});
+ render({locationRenderer([], mockData.complete, scopeId)});
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});
test('it renders emptyTagValue when invalid fields provided', () => {
render(
- {locationRenderer(['source.geo.my_house'], mockData.complete)}
+ {locationRenderer(['source.geo.my_house'], mockData.complete, scopeId)}
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
@@ -71,7 +75,11 @@ describe('Field Renderers', () => {
test('it renders correctly against snapshot', () => {
const { asFragment } = render(
- autonomousSystemRenderer(mockData.complete.source!.autonomousSystem!, FlowTarget.source),
+ autonomousSystemRenderer(
+ mockData.complete.source!.autonomousSystem!,
+ FlowTarget.source,
+ scopeId
+ ),
{ wrapper: TestProviders }
);
@@ -80,14 +88,18 @@ describe('Field Renderers', () => {
test('it renders emptyTagValue when non-string field provided', () => {
render(
- {autonomousSystemRenderer(halfEmptyMock, FlowTarget.source)}
+
+ {autonomousSystemRenderer(halfEmptyMock, FlowTarget.source, scopeId)}
+
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});
test('it renders emptyTagValue when invalid field provided', () => {
render(
- {autonomousSystemRenderer(emptyMock, FlowTarget.source)}
+
+ {autonomousSystemRenderer(emptyMock, FlowTarget.source, scopeId)}
+
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/field_renderers/field_renderers.tsx b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/field_renderers/field_renderers.tsx
index 2397a7f3bd13e..3bef8aa5bcdb8 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/field_renderers/field_renderers.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/field_renderers/field_renderers.tsx
@@ -16,7 +16,7 @@ import type {
FlowTargetSourceDest,
NetworkDetailsStrategyResponse,
} from '../../../../../common/search_strategy';
-import { DefaultDraggable } from '../../../../common/components/draggables';
+import { CellActionsRenderer } from '../../../../common/components/cell_actions/cell_actions_renderer';
import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { ReputationLink, WhoIsLink } from '../../../../common/components/links';
import * as i18n from '../details/translations';
@@ -28,7 +28,7 @@ export const IpOverviewId = 'ip-overview';
export const locationRenderer = (
fieldNames: string[],
data: NetworkDetailsStrategyResponse['networkDetails'],
- contextID?: string
+ scopeId: string
): React.ReactElement =>
fieldNames.length > 0 && fieldNames.every((fieldName) => getOr(null, fieldName, data)) ? (
@@ -38,15 +38,7 @@ export const locationRenderer = (
{index ? ',\u00A0' : ''}
-
+
);
@@ -59,29 +51,23 @@ export const locationRenderer = (
export const autonomousSystemRenderer = (
as: AutonomousSystem,
flowTarget: FlowTarget | FlowTargetSourceDest,
- contextID?: string
+ scopeId: string
): React.ReactElement =>
as && as.organization && as.organization.name && as.number ? (
-
{'/'}
-
@@ -143,34 +129,23 @@ interface HostNameRendererTypes {
scopeId: SourcererScopeName;
host: HostEcs;
ipFilter?: string;
- contextID?: string;
isFlyoutOpen: boolean;
}
export const hostNameRenderer = ({
scopeId,
host,
ipFilter,
- contextID,
isFlyoutOpen,
}: HostNameRendererTypes): React.ReactElement =>
host.name && host.name[0] && host.ip && (!(ipFilter != null) || host.ip.includes(ipFilter)) ? (
-
+
-
+
) : (
getEmptyTagValue()
);
diff --git a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/geo_fields.tsx b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/geo_fields.tsx
index 9cd456e1d2cd6..cf6a241c991d0 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/geo_fields.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/geo_fields.tsx
@@ -10,7 +10,7 @@ import { get, uniq } from 'lodash/fp';
import React from 'react';
import styled from '@emotion/styled';
-import { DefaultDraggable } from '../../../../common/components/draggables';
+import { CellActionsRenderer } from '../../../../common/components/cell_actions/cell_actions_renderer';
import { CountryFlag } from './country_flag';
import type { GeoFieldsProps, SourceDestinationType } from './types';
@@ -89,11 +89,9 @@ const GeoFieldValues = React.memo<{
) : null}
-
diff --git a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/index.test.tsx
index 19beb68192637..6c9bd4d76c3cd 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/index.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/index.test.tsx
@@ -45,16 +45,24 @@ import {
NETWORK_PROTOCOL_FIELD_NAME,
NETWORK_TRANSPORT_FIELD_NAME,
} from './field_names';
-import { CellActionsWrapper } from '../../../../common/components/drag_and_drop/cell_actions_wrapper';
+import { SecurityCellActions } from '../../../../common/components/cell_actions';
-jest.mock('../../../../common/components/drag_and_drop/cell_actions_wrapper', () => {
+jest.mock('../../../../common/components/cell_actions', () => {
return {
- CellActionsWrapper: jest.fn(),
+ SecurityCellActions: jest.fn(),
+ CellActionsMode: {
+ HOVER_DOWN: 'hover-down',
+ HOVER_RIGHT: 'hover-right',
+ INLINE: 'inline',
+ },
+ SecurityCellActionsTrigger: {
+ DEFAULT: 'default',
+ },
};
});
-const MockedCellActionsWrapper = jest.fn(({ children }) => {
- return {children}
;
+const MockedSecurityCellActions = jest.fn(({ children }) => {
+ return {children}
;
});
jest.mock('../../../../common/lib/kibana');
@@ -131,7 +139,7 @@ jest.mock('react-router-dom', () => {
describe('SourceDestination', () => {
beforeEach(() => {
- (CellActionsWrapper as unknown as jest.Mock).mockImplementation(MockedCellActionsWrapper);
+ (SecurityCellActions as unknown as jest.Mock).mockImplementation(MockedSecurityCellActions);
});
test('renders correctly against snapshot', () => {
@@ -337,9 +345,11 @@ describe('SourceDestination', () => {
test('should passing correct scopeId to cell actions', () => {
render({getSourceDestinationInstance()});
- expect(MockedCellActionsWrapper).toHaveBeenCalledWith(
+ expect(MockedSecurityCellActions).toHaveBeenCalledWith(
expect.objectContaining({
- scopeId: 'some_scope',
+ metadata: expect.objectContaining({
+ scopeId: 'some_scope',
+ }),
}),
{}
);
diff --git a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/network.tsx b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/network.tsx
index 441438912feb2..c976b80be70ec 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/network.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/network.tsx
@@ -11,7 +11,8 @@ import React from 'react';
import styled from '@emotion/styled';
import { DirectionBadge } from '../direction';
-import { DefaultDraggable, DraggableBadge } from '../../../../common/components/draggables';
+import { CellActionsRenderer } from '../../../../common/components/cell_actions/cell_actions_renderer';
+import { DraggableBadge } from '../../../../common/components/draggables';
import * as i18n from './translations';
import {
@@ -95,18 +96,13 @@ export const Network = React.memo<{
? uniq(bytes).map((b) =>
!isNaN(Number(b)) ? (
-
+
-
+
) : null
)
@@ -115,16 +111,11 @@ export const Network = React.memo<{
{packets != null
? uniq(packets).map((p) => (
-
+
{`${p} ${i18n.PACKETS}`}
-
+
))
: null}
diff --git a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/source_destination_arrows.tsx b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/source_destination_arrows.tsx
index f4839dec9ba12..c72f62a53ce14 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/source_destination_arrows.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/explore/network/components/source_destination/source_destination_arrows.tsx
@@ -17,7 +17,7 @@ import {
getPercent,
hasOneValue,
} from '../arrows/helpers';
-import { DefaultDraggable } from '../../../../common/components/draggables';
+import { CellActionsRenderer } from '../../../../common/components/cell_actions/cell_actions_renderer';
import { PreferenceFormattedBytes } from '../../../../common/components/formatted_bytes';
import * as i18n from './translations';
@@ -74,10 +74,9 @@ const SourceArrow = React.memo<{
{sourceBytes != null && !isNaN(Number(sourceBytes)) ? (
-
@@ -88,7 +87,7 @@ const SourceArrow = React.memo<{
-
+
) : null}
@@ -98,16 +97,15 @@ const SourceArrow = React.memo<{
{sourcePackets != null && !isNaN(Number(sourcePackets)) ? (
-
{`${sourcePackets} ${i18n.PACKETS}`}
-
+
) : null}
@@ -161,10 +159,9 @@ const DestinationArrow = React.memo<{
{destinationBytes != null && !isNaN(Number(destinationBytes)) ? (
-
@@ -175,7 +172,7 @@ const DestinationArrow = React.memo<{
-
+
) : null}
@@ -185,16 +182,15 @@ const DestinationArrow = React.memo<{
{destinationPackets != null && !isNaN(Number(destinationPackets)) ? (
-
{`${numeral(destinationPackets).format('0,0')} ${i18n.PACKETS}`}
-
+
) : null}
diff --git a/x-pack/solutions/security/plugins/security_solution/public/overview/components/host_overview/__snapshots__/index.test.tsx.snap b/x-pack/solutions/security/plugins/security_solution/public/overview/components/host_overview/__snapshots__/index.test.tsx.snap
index 44f1a76b79e76..a2c0e42a798f1 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/overview/components/host_overview/__snapshots__/index.test.tsx.snap
+++ b/x-pack/solutions/security/plugins/security_solution/public/overview/components/host_overview/__snapshots__/index.test.tsx.snap
@@ -34,14 +34,6 @@ exports[`Host Summary Component it renders the default Host Summary 1`] = `
opacity: 1;
}
-.c1 > span.euiToolTipAnchor {
- display: block;
-}
-
-.c1 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
span.euiToolTipAnchor {
- display: block;
-}
-
-.c1 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
span.euiToolTipAnchor {
- display: block;
-}
-
-.c1 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
span.euiToolTipAnchor {
- display: block;
-}
-
-.c1 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
>
)}
{typeof rowItem === 'string' && (
-
+
{render ? render(rowItem) : rowItem}
-
+
)}
);
diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx
index 791917e9da60f..b81f28bcc478a 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx
@@ -37,14 +37,6 @@ jest.mock('../../../common/lib/kibana/kibana_react', () => {
};
});
-jest.mock('../../../common/components/drag_and_drop/draggable_wrapper', () => {
- const original = jest.requireActual('../../../common/components/drag_and_drop/draggable_wrapper');
- return {
- ...original,
- DraggableWrapper: () => ,
- };
-});
-
jest.mock('../../store');
const mockOpenFlyout = jest.fn();
diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap
index 8b1fe8d554b4d..7168d972a3523 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap
+++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap
@@ -2,23 +2,15 @@
exports[`Netflow renders correctly against snapshot 1`] = `
- .c1 > span.euiToolTipAnchor {
- display: block;
-}
-
-.c1 > span.euiToolTipAnchor.eui-textTruncate {
- display: inline-block;
-}
-
-.c4 {
+ .c3 {
margin-right: 5px;
}
-.c3 {
+.c2 {
margin-right: 5px;
}
-.c2 {
+.c1 {
margin-right: 3px;
position: relative;
top: -1px;
@@ -50,14 +42,13 @@ exports[`Netflow renders correctly against snapshot 1`] = `
class="euiFlexItem emotion-euiFlexItem-growZero"
>