Skip to content

Commit

Permalink
[7.x] [Security Solution][Detection Page] Status filter refactor (#10…
Browse files Browse the repository at this point in the history
…7249) (#107995)

* [Security Solution][Detection Page] Status filter refactor (#107249)

* fixes merge conflicts

Co-authored-by: Davis Plumlee <[email protected]>
Co-authored-by: Davis Plumlee <[email protected]>
  • Loading branch information
3 people authored Aug 10, 2021
1 parent 65796d2 commit 0b21c67
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React from 'react';
import { waitFor, act } from '@testing-library/react';
import { mount } from 'enzyme';

import { esQuery } from '../../../../../../../../src/plugins/data/public';
import { esQuery, Filter } from '../../../../../../../../src/plugins/data/public';
import { TestProviders } from '../../../../common/mock';
import { SecurityPageName } from '../../../../app/types';

Expand Down Expand Up @@ -78,6 +78,11 @@ describe('AlertsHistogramPanel', () => {
updateDateRange: jest.fn(),
};

afterEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});

it('renders correctly', () => {
const wrapper = mount(
<TestProviders>
Expand Down Expand Up @@ -157,7 +162,7 @@ describe('AlertsHistogramPanel', () => {
combinedQueries:
'{"bool":{"must":[],"filter":[{"match_all":{}},{"exists":{"field":"process.name"}}],"should":[],"must_not":[]}}',
};
mount(
const wrapper = mount(
<TestProviders>
<AlertsHistogramPanel {...props} />
</TestProviders>
Expand All @@ -180,6 +185,60 @@ describe('AlertsHistogramPanel', () => {
],
]);
});
wrapper.unmount();
});
});

describe('Filters', () => {
it('filters props is valid, alerts query include filter', async () => {
const mockGetAlertsHistogramQuery = jest.spyOn(helpers, 'getAlertsHistogramQuery');
const statusFilter: Filter = {
meta: {
alias: null,
disabled: false,
key: 'signal.status',
negate: false,
params: {
query: 'open',
},
type: 'phrase',
},
query: {
term: {
'signal.status': 'open',
},
},
};

const props = {
...defaultProps,
query: { query: '', language: 'kql' },
filters: [statusFilter],
};
const wrapper = mount(
<TestProviders>
<AlertsHistogramPanel {...props} />
</TestProviders>
);

await waitFor(() => {
expect(mockGetAlertsHistogramQuery.mock.calls[1]).toEqual([
'signal.rule.name',
'2020-07-07T08:20:18.966Z',
'2020-07-08T08:20:18.966Z',
[
{
bool: {
filter: [{ term: { 'signal.status': 'open' } }],
must: [],
must_not: [],
should: [],
},
},
],
]);
});
wrapper.unmount();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,27 @@
*/

import { EuiFilterButton, EuiFilterGroup } from '@elastic/eui';
import { rgba } from 'polished';
import React, { useCallback, useState } from 'react';
import styled from 'styled-components';
import { Status } from '../../../../../common/detection_engine/schemas/common/schemas';
import * as i18n from '../translations';

export const FILTER_OPEN: Status = 'open';
export const FILTER_CLOSED: Status = 'closed';
export const FILTER_IN_PROGRESS: Status = 'in-progress';

const StatusFilterButton = styled(EuiFilterButton)<{ isActive: boolean }>`
background: ${({ isActive, theme }) => (isActive ? theme.eui.euiColorPrimary : '')};
`;

const StatusFilterGroup = styled(EuiFilterGroup)`
background: ${({ theme }) => rgba(theme.eui.euiColorPrimary, 0.2)};
.euiButtonEmpty--ghost:enabled:focus {
background-color: ${({ theme }) => theme.eui.euiColorPrimary};
}
`;

interface Props {
onFilterGroupChanged: (filterGroup: Status) => void;
}
Expand All @@ -37,33 +50,39 @@ const AlertsTableFilterGroupComponent: React.FC<Props> = ({ onFilterGroupChanged
}, [setFilterGroup, onFilterGroupChanged]);

return (
<EuiFilterGroup data-test-subj="alerts-table-filter-group">
<EuiFilterButton
<StatusFilterGroup data-test-subj="alerts-table-filter-group">
<StatusFilterButton
data-test-subj="openAlerts"
hasActiveFilters={filterGroup === FILTER_OPEN}
isActive={filterGroup === FILTER_OPEN}
onClick={onClickOpenFilterCallback}
withNext
color={filterGroup === FILTER_OPEN ? 'ghost' : 'primary'}
>
{i18n.OPEN_ALERTS}
</EuiFilterButton>
</StatusFilterButton>

<EuiFilterButton
<StatusFilterButton
data-test-subj="inProgressAlerts"
hasActiveFilters={filterGroup === FILTER_IN_PROGRESS}
isActive={filterGroup === FILTER_IN_PROGRESS}
onClick={onClickInProgressFilterCallback}
withNext
color={filterGroup === FILTER_IN_PROGRESS ? 'ghost' : 'primary'}
>
{i18n.IN_PROGRESS_ALERTS}
</EuiFilterButton>
</StatusFilterButton>

<EuiFilterButton
<StatusFilterButton
data-test-subj="closedAlerts"
hasActiveFilters={filterGroup === FILTER_CLOSED}
isActive={filterGroup === FILTER_CLOSED}
onClick={onClickCloseFilterCallback}
color={filterGroup === FILTER_CLOSED ? 'ghost' : 'primary'}
>
{i18n.CLOSED_ALERTS}
</EuiFilterButton>
</EuiFilterGroup>
</StatusFilterButton>
</StatusFilterGroup>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ describe('AlertsTableComponent', () => {
isSelectAllChecked={false}
clearSelected={jest.fn()}
setEventsLoading={jest.fn()}
clearEventsLoading={jest.fn()}
setEventsDeleted={jest.fn()}
clearEventsDeleted={jest.fn()}
showBuildingBlockAlerts={false}
onShowBuildingBlockAlertsChanged={jest.fn()}
showOnlyThreatIndicatorAlerts={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import {
alertsDefaultModelRuleRegistry,
buildAlertStatusFilterRuleRegistry,
} from './default_config';
import { FILTER_OPEN, AlertsTableFilterGroup } from './alerts_filter_group';
import { AditionalFiltersAction, AlertsUtilityBar } from './alerts_utility_bar';
import * as i18nCommon from '../../../common/translations';
import * as i18n from './translations';
Expand Down Expand Up @@ -68,13 +67,12 @@ interface OwnProps {
showOnlyThreatIndicatorAlerts: boolean;
timelineId: TimelineIdLiteral;
to: string;
filterGroup?: Status;
}

type AlertsTableComponentProps = OwnProps & PropsFromRedux;

export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
clearEventsDeleted,
clearEventsLoading,
clearSelected,
defaultFilters,
from,
Expand All @@ -95,10 +93,10 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
showOnlyThreatIndicatorAlerts,
timelineId,
to,
filterGroup = 'open',
}) => {
const dispatch = useDispatch();
const [showClearSelectionAction, setShowClearSelectionAction] = useState(false);
const [filterGroup, setFilterGroup] = useState<Status>(FILTER_OPEN);
const {
browserFields,
indexPattern: indexPatterns,
Expand Down Expand Up @@ -216,17 +214,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
}
}, [dispatch, isSelectAllChecked, timelineId]);

// Callback for when open/closed filter changes
const onFilterGroupChangedCallback = useCallback(
(newFilterGroup: Status) => {
clearEventsLoading!({ id: timelineId });
clearEventsDeleted!({ id: timelineId });
clearSelected!({ id: timelineId });
setFilterGroup(newFilterGroup);
},
[clearEventsLoading, clearEventsDeleted, clearSelected, setFilterGroup, timelineId]
);

// Callback for clearing entire selection from utility bar
const clearSelectionCallback = useCallback(() => {
clearSelected!({ id: timelineId });
Expand Down Expand Up @@ -382,11 +369,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
);
}, [dispatch, defaultTimelineModel, filterManager, tGridEnabled, timelineId]);

const headerFilterGroup = useMemo(
() => <AlertsTableFilterGroup onFilterGroupChanged={onFilterGroupChangedCallback} />,
[onFilterGroupChangedCallback]
);

if (loading || indexPatternsLoading || isEmpty(selectedPatterns)) {
return (
<EuiPanel hasBorder>
Expand All @@ -403,7 +385,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
defaultModel={defaultTimelineModel}
end={to}
currentFilter={filterGroup}
headerFilterGroup={headerFilterGroup}
id={timelineId}
onRuleChange={onRuleChange}
renderCellValue={RenderCellValue}
Expand Down Expand Up @@ -449,8 +430,6 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
eventIds: string[];
isLoading: boolean;
}) => dispatch(timelineActions.setEventsLoading({ id, eventIds, isLoading })),
clearEventsLoading: ({ id }: { id: string }) =>
dispatch(timelineActions.clearEventsLoading({ id })),
setEventsDeleted: ({
id,
eventIds,
Expand All @@ -460,8 +439,6 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
eventIds: string[];
isDeleted: boolean;
}) => dispatch(timelineActions.setEventsDeleted({ id, eventIds, isDeleted })),
clearEventsDeleted: ({ id }: { id: string }) =>
dispatch(timelineActions.clearEventsDeleted({ id })),
});

const connector = connect(makeMapStateToProps, mapDispatchToProps);
Expand Down
Loading

0 comments on commit 0b21c67

Please sign in to comment.