diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx index 833688ae57993..6f77d15913d07 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx @@ -50,6 +50,10 @@ const utilityBar = (refetch: inputsModel.Refetch, totalCount: number) => (
); +const exceptionsModal = (refetch: inputsModel.Refetch) => ( +
+); + const eventsViewerDefaultProps = { browserFields: {}, columns: [], @@ -460,4 +464,42 @@ describe('EventsViewer', () => { }); }); }); + + describe('exceptions modal', () => { + test('it renders exception modal if "exceptionsModal" callback exists', async () => { + const wrapper = mount( + + + + + + ); + + await waitFor(() => { + wrapper.update(); + + expect(wrapper.find(`[data-test-subj="mock-exceptions-modal"]`).exists()).toBeTruthy(); + }); + }); + + test('it does not render exception modal if "exceptionModal" callback does not exist', async () => { + const wrapper = mount( + + + + + + ); + + await waitFor(() => { + wrapper.update(); + + expect(wrapper.find(`[data-test-subj="mock-exceptions-modal"]`).exists()).toBeFalsy(); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 436386077e725..ebda64efabf65 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -109,6 +109,7 @@ interface Props { utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode; // If truthy, the graph viewer (Resolver) is showing graphEventId: string | undefined; + exceptionsModal?: (refetch: inputsModel.Refetch) => React.ReactNode; } const EventsViewerComponent: React.FC = ({ @@ -134,6 +135,7 @@ const EventsViewerComponent: React.FC = ({ toggleColumn, utilityBar, graphEventId, + exceptionsModal, }) => { const { globalFullScreen } = useFullScreen(); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; @@ -259,6 +261,7 @@ const EventsViewerComponent: React.FC = ({ )} + {exceptionsModal && exceptionsModal(refetch)} {utilityBar && !resolverIsShowing(graphEventId) && ( {utilityBar?.(refetch, totalCountMinusDeleted)} )} @@ -335,5 +338,6 @@ export const EventsViewer = React.memo( prevProps.start === nextProps.start && prevProps.sort === nextProps.sort && prevProps.utilityBar === nextProps.utilityBar && - prevProps.graphEventId === nextProps.graphEventId + prevProps.graphEventId === nextProps.graphEventId && + prevProps.exceptionsModal === nextProps.exceptionsModal ); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index e4520dab4626a..ec56a3a1bd8d3 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -43,6 +43,7 @@ export interface OwnProps { headerFilterGroup?: React.ReactNode; pageFilters?: Filter[]; utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode; + exceptionsModal?: (refetch: inputsModel.Refetch) => React.ReactNode; } type Props = OwnProps & PropsFromRedux; @@ -74,6 +75,7 @@ const StatefulEventsViewerComponent: React.FC = ({ utilityBar, // If truthy, the graph viewer (Resolver) is showing graphEventId, + exceptionsModal, }) => { const [ { docValueFields, browserFields, indexPatterns, isLoading: isLoadingIndexPattern }, @@ -156,6 +158,7 @@ const StatefulEventsViewerComponent: React.FC = ({ toggleColumn={toggleColumn} utilityBar={utilityBar} graphEventId={graphEventId} + exceptionsModal={exceptionsModal} /> @@ -220,6 +223,7 @@ type PropsFromRedux = ConnectedProps; export const StatefulEventsViewer = connector( React.memo( StatefulEventsViewerComponent, + // eslint-disable-next-line complexity (prevProps, nextProps) => prevProps.id === nextProps.id && deepEqual(prevProps.columns, nextProps.columns) && @@ -240,6 +244,7 @@ export const StatefulEventsViewer = connector( prevProps.showCheckboxes === nextProps.showCheckboxes && prevProps.start === nextProps.start && prevProps.utilityBar === nextProps.utilityBar && - prevProps.graphEventId === nextProps.graphEventId + prevProps.graphEventId === nextProps.graphEventId && + prevProps.exceptionsModal === nextProps.exceptionsModal ) ); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 07e69d850f173..356cdfd84045e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -220,28 +220,6 @@ export const AlertsTableComponent: React.FC = ({ [dispatchToaster] ); - const openAddExceptionModalCallback = useCallback( - ({ - ruleName, - ruleIndices, - ruleId, - exceptionListType, - alertData, - }: AddExceptionModalBaseProps) => { - if (alertData !== null && alertData !== undefined) { - setShouldShowAddExceptionModal(true); - setAddExceptionModalState({ - ruleName, - ruleId, - ruleIndices, - exceptionListType, - alertData, - }); - } - }, - [setShouldShowAddExceptionModal, setAddExceptionModalState] - ); - // Catches state change isSelectAllChecked->false upon user selection change to reset utility bar useEffect(() => { if (isSelectAllChecked) { @@ -352,6 +330,74 @@ export const AlertsTableComponent: React.FC = ({ ] ); + const closeAddExceptionModal = useCallback(() => { + setShouldShowAddExceptionModal(false); + setAddExceptionModalState(addExceptionModalInitialState); + }, [setShouldShowAddExceptionModal, setAddExceptionModalState]); + + const onAddExceptionCancel = useCallback(() => { + closeAddExceptionModal(); + }, [closeAddExceptionModal]); + + const onAddExceptionConfirm = useCallback( + (refetch) => () => { + refetch(); + closeAddExceptionModal(); + }, + [closeAddExceptionModal] + ); + + const openAddExceptionModalCallback = useCallback( + ({ + ruleName, + ruleIndices, + ruleId, + exceptionListType, + alertData, + }: AddExceptionModalBaseProps) => { + if (alertData !== null && alertData !== undefined) { + setShouldShowAddExceptionModal(true); + setAddExceptionModalState({ + ruleName, + ruleId, + ruleIndices, + exceptionListType, + alertData, + }); + } + }, + [setShouldShowAddExceptionModal, setAddExceptionModalState] + ); + + // Callback for creating the AddExceptionModal and allowing it + // access to the refetchQuery to update the page + const exceptionModalCallback = useCallback( + (refetchQuery: inputsModel.Refetch) => { + if (shouldShowAddExceptionModal) { + return ( + + ); + } else { + return <>; + } + }, + [ + addExceptionModalState, + filterGroup, + onAddExceptionCancel, + onAddExceptionConfirm, + shouldShowAddExceptionModal, + ] + ); // Send to Timeline / Update Alert Status Actions for each table row const additionalActions = useMemo( () => ({ ecsData, nonEcsData }: TimelineRowActionArgs) => @@ -378,13 +424,13 @@ export const AlertsTableComponent: React.FC = ({ createTimelineCallback, dispatch, hasIndexWrite, - filterGroup, - setEventsLoadingCallback, + onAlertStatusUpdateFailure, + onAlertStatusUpdateSuccess, setEventsDeletedCallback, + setEventsLoadingCallback, + filterGroup, timelineId, updateTimelineIsLoading, - onAlertStatusUpdateSuccess, - onAlertStatusUpdateFailure, openAddExceptionModalCallback, ] ); @@ -432,19 +478,6 @@ export const AlertsTableComponent: React.FC = ({ [onFilterGroupChangedCallback] ); - const closeAddExceptionModal = useCallback(() => { - setShouldShowAddExceptionModal(false); - setAddExceptionModalState(addExceptionModalInitialState); - }, [setShouldShowAddExceptionModal, setAddExceptionModalState]); - - const onAddExceptionCancel = useCallback(() => { - closeAddExceptionModal(); - }, [closeAddExceptionModal]); - - const onAddExceptionConfirm = useCallback(() => closeAddExceptionModal(), [ - closeAddExceptionModal, - ]); - if (loading || indexPatternsLoading || isEmpty(signalsIndex)) { return ( @@ -465,19 +498,8 @@ export const AlertsTableComponent: React.FC = ({ id={timelineId} start={from} utilityBar={utilityBarCallback} + exceptionsModal={exceptionModalCallback} /> - {shouldShowAddExceptionModal === true && addExceptionModalState.alertData !== null && ( - - )} ); };