Skip to content

Commit

Permalink
enable actions in preview
Browse files Browse the repository at this point in the history
  • Loading branch information
christineweng committed Dec 12, 2024
1 parent 9312248 commit f22ab8f
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,47 @@ import { mockFlyoutApi } from '../shared/mocks/mock_flyout_context';
import { mockContextValue } from '../shared/mocks/mock_context';
import { DocumentDetailsContext } from '../shared/context';
import { PreviewPanelFooter } from './footer';
import { PREVIEW_FOOTER_TEST_ID, PREVIEW_FOOTER_LINK_TEST_ID } from './test_ids';
import { PREVIEW_FOOTER_LINK_TEST_ID } from './test_ids';
import { FLYOUT_FOOTER_TEST_ID, FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID } from '../right/test_ids';
import { createTelemetryServiceMock } from '../../../common/lib/telemetry/telemetry_service.mock';
import { useKibana } from '../../../common/lib/kibana';
import { useAlertExceptionActions } from '../../../detections/components/alerts_table/timeline_actions/use_add_exception_actions';
import { useInvestigateInTimeline } from '../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline';
import { useAddToCaseActions } from '../../../detections/components/alerts_table/timeline_actions/use_add_to_case_actions';

jest.mock('@kbn/expandable-flyout');

const mockedTelemetry = createTelemetryServiceMock();
jest.mock('../../../common/lib/kibana', () => {
jest.mock('react-router-dom', () => {
const original = jest.requireActual('react-router-dom');
return {
useKibana: () => ({
services: {
telemetry: mockedTelemetry,
},
}),
...original,
useLocation: jest.fn().mockReturnValue({ search: '' }),
};
});

jest.mock('../../../common/lib/kibana');
jest.mock('../../../detections/components/alerts_table/timeline_actions/use_add_exception_actions');
jest.mock(
'../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline'
);
jest.mock('../../../detections/components/alerts_table/timeline_actions/use_add_to_case_actions');

const mockedTelemetry = createTelemetryServiceMock();

describe('<PreviewPanelFooter />', () => {
beforeAll(() => {
beforeEach(() => {
jest.mocked(useExpandableFlyoutApi).mockReturnValue(mockFlyoutApi);
(useKibana as jest.Mock).mockReturnValue({
services: {
osquery: { isOsqueryAvailable: jest.fn() },
telemetry: mockedTelemetry,
cases: { hooks: { useIsAddToCaseOpen: jest.fn().mockReturnValue(false) } },
},
});
(useAlertExceptionActions as jest.Mock).mockReturnValue({ exceptionActionItems: [] });
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineActionItems: [],
});
(useAddToCaseActions as jest.Mock).mockReturnValue({ addToCaseActionItems: [] });
});

it('should render footer for alert', () => {
Expand All @@ -42,8 +64,8 @@ describe('<PreviewPanelFooter />', () => {
</DocumentDetailsContext.Provider>
</TestProviders>
);
expect(getByTestId(PREVIEW_FOOTER_TEST_ID)).toBeInTheDocument();
expect(getByTestId(PREVIEW_FOOTER_TEST_ID)).toHaveTextContent('Show full alert details');
expect(getByTestId(FLYOUT_FOOTER_TEST_ID)).toBeInTheDocument();
expect(getByTestId(PREVIEW_FOOTER_LINK_TEST_ID)).toHaveTextContent('Show full alert details');
});

it('should render footer for event', () => {
Expand All @@ -56,7 +78,21 @@ describe('<PreviewPanelFooter />', () => {
</DocumentDetailsContext.Provider>
</TestProviders>
);
expect(getByTestId(PREVIEW_FOOTER_TEST_ID)).toHaveTextContent('Show full event details');
expect(getByTestId(PREVIEW_FOOTER_LINK_TEST_ID)).toHaveTextContent('Show full event details');
});

it('should render the take action button', () => {
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineActionItems: [{ name: 'test', onClick: jest.fn() }],
});
const { getByTestId } = render(
<TestProviders>
<DocumentDetailsContext.Provider value={mockContextValue}>
<PreviewPanelFooter />
</DocumentDetailsContext.Provider>
</TestProviders>
);
expect(getByTestId(FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID)).toBeInTheDocument();
});

it('should open document details flyout when clicked', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
* 2.0.
*/

import { EuiLink, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiLink } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { getField } from '../shared/utils';
import { EventKind } from '../shared/constants/event_kinds';
import { FlyoutFooter } from '../../shared/components/flyout_footer';
import { DocumentDetailsRightPanelKey } from '../shared/constants/panel_keys';
import { useDocumentDetailsContext } from '../shared/context';
import { PREVIEW_FOOTER_TEST_ID, PREVIEW_FOOTER_LINK_TEST_ID } from './test_ids';
import { PREVIEW_FOOTER_LINK_TEST_ID } from './test_ids';
import { useKibana } from '../../../common/lib/kibana';
import { DocumentEventTypes } from '../../../common/lib/telemetry';
import { PanelFooter } from '../right/footer';

/**
* Footer at the bottom of preview panel with a link to open document details flyout
Expand Down Expand Up @@ -48,24 +48,22 @@ export const PreviewPanelFooter = () => {
});
}, [openFlyout, eventId, indexName, scopeId, telemetry]);

return (
<FlyoutFooter data-test-subj={PREVIEW_FOOTER_TEST_ID}>
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiLink
onClick={openDocumentFlyout}
target="_blank"
data-test-subj={PREVIEW_FOOTER_LINK_TEST_ID}
>
<>
{i18n.translate('xpack.securitySolution.flyout.preview.openFlyoutLabel', {
values: { isAlert },
defaultMessage: 'Show full {isAlert, select, true{alert} other{event}} details',
})}
</>
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</FlyoutFooter>
const additionalActions = useMemo(
() => (
<EuiLink
onClick={openDocumentFlyout}
target="_blank"
data-test-subj={PREVIEW_FOOTER_LINK_TEST_ID}
>
<>
{i18n.translate('xpack.securitySolution.flyout.preview.openFlyoutLabel', {
values: { isAlert },
defaultMessage: 'Show full {isAlert, select, true{alert} other{event}} details',
})}
</>
</EuiLink>
),
[isAlert, openDocumentFlyout]
);
return <PanelFooter additionalActions={additionalActions} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
ALERT_ASSIGNEES_CONTEXT_MENU_ITEM_TITLE,
ALERT_TAGS_CONTEXT_MENU_ITEM_TITLE,
} from '../../../../common/components/toolbar/bulk_actions/translations';
import { FLYOUT_FOOTER_DEOPDOEN_BUTTON_TEST_ID } from '../test_ids';
import { FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID } from '../test_ids';

jest.mock('../../../../common/components/endpoint/host_isolation');
jest.mock('../../../../common/components/endpoint/responder');
Expand Down Expand Up @@ -128,7 +128,7 @@ describe('take action dropdown', () => {
</TestProviders>
);
expect(
wrapper.find(`[data-test-subj="${FLYOUT_FOOTER_DEOPDOEN_BUTTON_TEST_ID}"]`).exists()
wrapper.find(`[data-test-subj="${FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID}"]`).exists()
).toBeTruthy();
});

Expand All @@ -139,7 +139,7 @@ describe('take action dropdown', () => {
</TestProviders>
);
expect(
wrapper.find(`[data-test-subj="${FLYOUT_FOOTER_DEOPDOEN_BUTTON_TEST_ID}"]`).first().text()
wrapper.find(`[data-test-subj="${FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID}"]`).first().text()
).toEqual('Take action');
});

Expand All @@ -153,7 +153,7 @@ describe('take action dropdown', () => {
</TestProviders>
);
wrapper
.find(`button[data-test-subj="${FLYOUT_FOOTER_DEOPDOEN_BUTTON_TEST_ID}"]`)
.find(`button[data-test-subj="${FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID}"]`)
.simulate('click');
});
test('should render "Add to existing case"', async () => {
Expand Down Expand Up @@ -325,7 +325,7 @@ describe('take action dropdown', () => {
</TestProviders>
);
wrapper
.find(`button[data-test-subj="${FLYOUT_FOOTER_DEOPDOEN_BUTTON_TEST_ID}"]`)
.find(`button[data-test-subj="${FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID}"]`)
.simulate('click');

return wrapper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs';
import { TableId } from '@kbn/securitysolution-data-table';
import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
import { i18n } from '@kbn/i18n';
import { FLYOUT_FOOTER_DEOPDOEN_BUTTON_TEST_ID } from '../test_ids';
import { FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID } from '../test_ids';
import { getAlertDetailsFieldValue } from '../../../../common/lib/endpoint/utils/get_event_details_field_values';
import { GuidedOnboardingTourStep } from '../../../../common/components/guided_onboarding_tour/tour_step';
import {
Expand Down Expand Up @@ -362,7 +362,7 @@ export const TakeActionDropdown = memo(
tourId={SecurityStepId.alertsCases}
>
<EuiButton
data-test-subj={FLYOUT_FOOTER_DEOPDOEN_BUTTON_TEST_ID}
data-test-subj={FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID}
fill
iconSide="right"
iconType="arrowDown"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,48 @@ import { PanelFooter } from './footer';
import { TestProviders } from '../../../common/mock';
import { mockContextValue } from '../shared/mocks/mock_context';
import { DocumentDetailsContext } from '../shared/context';
import { FLYOUT_FOOTER_TEST_ID } from './test_ids';
import { FLYOUT_FOOTER_TEST_ID, FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID } from './test_ids';
import { useKibana } from '../../../common/lib/kibana';
import { useAlertExceptionActions } from '../../../detections/components/alerts_table/timeline_actions/use_add_exception_actions';
import { useInvestigateInTimeline } from '../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline';
import { useAddToCaseActions } from '../../../detections/components/alerts_table/timeline_actions/use_add_to_case_actions';

jest.mock('../../../common/lib/kibana');
jest.mock('react-router-dom', () => {
const original = jest.requireActual('react-router-dom');
return {
...original,
useLocation: jest.fn().mockReturnValue({ search: '' }),
};
});
jest.mock('../../../detections/components/alerts_table/timeline_actions/use_add_exception_actions');
jest.mock(
'../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline'
);
jest.mock('../../../detections/components/alerts_table/timeline_actions/use_add_to_case_actions');

describe('PanelFooter', () => {
it('should not render the take action dropdown if preview mode', () => {
const { queryByTestId } = render(
<TestProviders>
<DocumentDetailsContext.Provider value={mockContextValue}>
<PanelFooter isPreview={true} />
</DocumentDetailsContext.Provider>
</TestProviders>
);

expect(queryByTestId(FLYOUT_FOOTER_TEST_ID)).not.toBeInTheDocument();
});

it('should render the take action dropdown', () => {
(useKibana as jest.Mock).mockReturnValue({
services: {
osquery: {
isOsqueryAvailable: jest.fn(),
},
osquery: { isOsqueryAvailable: jest.fn() },
cases: { hooks: { useIsAddToCaseOpen: jest.fn().mockReturnValue(false) } },
},
});
(useAlertExceptionActions as jest.Mock).mockReturnValue({ exceptionActionItems: [] });
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineActionItems: [],
investigateInTimelineActionItems: [{ name: 'test', onClick: jest.fn() }],
});
(useAddToCaseActions as jest.Mock).mockReturnValue({ addToCaseActionItems: [] });

const wrapper = render(
<TestProviders>
<DocumentDetailsContext.Provider value={mockContextValue}>
<PanelFooter isPreview={false} />
<PanelFooter />
</DocumentDetailsContext.Provider>
</TestProviders>
);
expect(wrapper.getByTestId(FLYOUT_FOOTER_TEST_ID)).toBeInTheDocument();
expect(wrapper.getByTestId(FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID)).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ interface AlertSummaryData {

interface PanelFooterProps {
/**
* Boolean that indicates whether flyout is in preview and action should be hidden
* Additional actions to be displayed in the footer
*/
isPreview: boolean;
additionalActions?: React.ReactNode;
}

/**
* Bottom section of the flyout that contains the take action button
*/
export const PanelFooter: FC<PanelFooterProps> = ({ isPreview }) => {
export const PanelFooter: FC<PanelFooterProps> = ({ additionalActions }) => {
const { euiTheme } = useEuiTheme();
// we need this flyout to be above the timeline flyout (which has a z-index of 1002)
const flyoutZIndex = useMemo(
Expand Down Expand Up @@ -172,8 +172,6 @@ export const PanelFooter: FC<PanelFooterProps> = ({ isPreview }) => {
[dataAsNestedObject?._id, dataAsNestedObject?.kibana?.alert]
);

if (isPreview) return null;

return (
<>
<EuiFlyoutFooter data-test-subj={FLYOUT_FOOTER_TEST_ID}>
Expand All @@ -196,6 +194,7 @@ export const PanelFooter: FC<PanelFooterProps> = ({ isPreview }) => {
/>
)}
</EuiFlexItem>
{additionalActions && <EuiFlexItem grow={false}>{additionalActions}</EuiFlexItem>}
</EuiFlexGroup>
</EuiPanel>
</EuiFlyoutFooter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { PREFIX } from '../../shared/test_ids';

export const FLYOUT_BODY_TEST_ID = `${PREFIX}Body` as const;
export const FLYOUT_FOOTER_TEST_ID = `${PREFIX}Footer` as const;
export const FLYOUT_FOOTER_DEOPDOEN_BUTTON_TEST_ID =
export const FLYOUT_FOOTER_DROPDOWN_BUTTON_TEST_ID =
`${FLYOUT_FOOTER_TEST_ID}DropdownButton` as const;
export const OVERVIEW_TAB_TEST_ID = `${PREFIX}OverviewTab` as const;
export const TABLE_TAB_TEST_ID = `${PREFIX}TableTab` as const;
Expand Down

0 comments on commit f22ab8f

Please sign in to comment.