Skip to content

Commit

Permalink
Correlations page improvements (#855) (#867)
Browse files Browse the repository at this point in the history
* ux improvements



* updated text styles



---------


(cherry picked from commit cebf9cd)

Signed-off-by: Amardeepsingh Siglani <[email protected]>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
1 parent 2399fc1 commit 9a63df0
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 62 deletions.
85 changes: 71 additions & 14 deletions public/pages/Correlations/components/FilterGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
EuiFieldSearch,
FilterChecked,
EuiPopoverFooter,
EuiButtonEmpty,
EuiButtonGroup,
} from '@elastic/eui';

export type FilterItem = {
Expand All @@ -31,6 +31,19 @@ export interface LogTypeFilterGroupProps {
setItems: (items: FilterItem[]) => void;
}

type SelectionToggleOptionIds = 'select_all' | 'deselect_all';

const selectionToggleButtons = [
{
id: 'select_all',
label: 'Select all',
},
{
id: 'deselect_all',
label: 'Deselect all',
},
];

export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({
groupName,
items,
Expand All @@ -39,7 +52,10 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({
setItems,
}) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [showActiveFilters, setShowActiveFilters] = useState(false);
const [filterUpdated, setFilterUpdated] = useState(false);
const [selectionToggleSelectedId, setSelectionToggleSelectedId] = useState<
SelectionToggleOptionIds
>('select_all');

const onButtonClick = () => {
setIsPopoverOpen(!isPopoverOpen);
Expand All @@ -62,14 +78,48 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({
...newItems[index],
checked: undefined,
};

// This means, it is not a grouping label,
// so we need to switch off the grouping label too if it exists
if (!newItems[index].childOptionIds) {
for (let i = index - 1; i >= 0; i--) {
// Found the parent grouping label
if (newItems[i].childOptionIds) {
newItems[i].checked = undefined;
break;
}
}
}
break;

default:
newItems[index] = {
...newItems[index],
checked: 'on',
};

// This means, it is not a grouping label,
// so we need to switch ON the grouping label if all children are checked
if (!newItems[index].childOptionIds) {
let i: number;
for (i = index - 1; i >= 0; i--) {
const childIds = newItems[i].childOptionIds;
// Found the parent grouping label
if (childIds) {
let allChecked = true;
newItems.forEach((item) => {
if (childIds.has(item.id)) {
allChecked = allChecked && !!item.checked;
}
});
newItems[i].checked = allChecked ? 'on' : undefined;
break;
}
}
}
}

// If a grouping label is toggled, toggle all its children
const childIds = newItems[index].childOptionIds;
if (childIds) {
newItems.forEach((item) => {
Expand All @@ -80,7 +130,12 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({
}

setItems(newItems);
setShowActiveFilters(true);
setFilterUpdated(!newItems.every((item) => item.checked));
}

function onSelectionToggleChange(optionId: string) {
setSelectionToggleSelectedId(optionId as SelectionToggleOptionIds);
toggleAll(optionId === 'select_all' ? 'on' : undefined);
}

function toggleAll(state: 'on' | undefined) {
Expand All @@ -90,7 +145,7 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({
}));

setItems(newItems);
setShowActiveFilters(!!state);
setFilterUpdated(!state);
}

function search(term: string) {
Expand All @@ -100,18 +155,18 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({
item.visible = item.id.toLowerCase().includes(term);
});
setItems(newItems);
setShowActiveFilters(true);
setFilterUpdated(true);
}

const numActiveFilters = items.filter((item) => !item.childOptionIds && item.checked === 'on')
.length;
const button = (
<EuiFilterButton
iconType="arrowDown"
onClick={onButtonClick}
isSelected={isPopoverOpen}
hasActiveFilters={showActiveFilters && !!items.find((item) => item.checked === 'on')}
numActiveFilters={
showActiveFilters ? items.filter((item) => item.checked === 'on').length : undefined
}
hasActiveFilters={filterUpdated}
numActiveFilters={numActiveFilters > 0 ? numActiveFilters : undefined}
>
{groupName}
</EuiFilterButton>
Expand All @@ -127,7 +182,7 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({
panelPaddingSize="none"
>
<EuiPopoverTitle paddingSize="s">
<EuiFieldSearch compressed onSearch={search} />
<EuiFieldSearch compressed onSearch={search} isClearable={true} />
</EuiPopoverTitle>
<div
className="ouiFilterSelect__items"
Expand All @@ -154,10 +209,12 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({
</div>
{hasFooter && (
<EuiPopoverFooter>
<div>
<EuiButtonEmpty onClick={() => toggleAll('on')}>Select all</EuiButtonEmpty>
<EuiButtonEmpty onClick={() => toggleAll(undefined)}>Deselect all</EuiButtonEmpty>
</div>
<EuiButtonGroup
legend="All toptions selection toggle group"
options={selectionToggleButtons}
idSelected={selectionToggleSelectedId}
onChange={onSelectionToggleChange}
/>
</EuiPopoverFooter>
)}
</EuiPopover>
Expand Down
36 changes: 26 additions & 10 deletions public/pages/Correlations/components/FindingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
EuiHorizontalRule,
EuiToolTip,
EuiDescriptionList,
EuiText,
EuiIcon,
} from '@elastic/eui';
import { getSeverityLabel, getSeverityColor, getLabelFromLogType } from '../utils/constants';
import { DataStore } from '../../../store/DataStore';
Expand Down Expand Up @@ -43,19 +45,19 @@ export const FindingCard: React.FC<FindingCardProps> = ({
}) => {
const list = [
{
title: 'Detection rule',
description: detectionRule.name,
title: <b>Detection rule</b>,
description: <EuiText size="s">{detectionRule.name}</EuiText>,
},
];

if (finding.correlationRule) {
list.unshift({
title: 'Correlation score',
description: finding.correlationRule.name,
title: <b>Correlation rule</b>,
description: <EuiText size="s">{finding.correlationRule.name}</EuiText>,
});
}

const badgePadding = '2px 7px';
const badgePadding = '0px 4px';
const { text: severityText, background } = getSeverityColor(detectionRule.severity);

const header = (
Expand All @@ -65,7 +67,7 @@ export const FindingCard: React.FC<FindingCardProps> = ({
<EuiBadge color={background} style={{ padding: badgePadding, color: severityText }}>
{getSeverityLabel(detectionRule.severity)}
</EuiBadge>
<EuiBadge color="hollow" style={{ padding: '3px 10px' }}>
<EuiBadge color="hollow" style={{ padding: '0px 4px' }}>
{getLabelFromLogType(logType)}
</EuiBadge>
</div>
Expand All @@ -90,21 +92,35 @@ export const FindingCard: React.FC<FindingCardProps> = ({
</EuiFlexGroup>
);

const attrList = <EuiDescriptionList type="column" textStyle="reverse" listItems={list} />;
const attrList = (
<EuiDescriptionList type="column" textStyle="reverse" listItems={list} compressed />
);

const relatedFindingCard = (
<EuiPanel>
{header}
<EuiSpacer size="s" />
<EuiFlexGroup justifyContent="spaceBetween" gutterSize="xs" alignItems="center">
<EuiFlexItem grow={false}>
<p>Correlation score</p>
<EuiText size="s">
Correlation score{' '}
<EuiToolTip
content={`The score (0-1) is based on the proximity of relevant findings in the threat scenario defined by the
correlation rule. The greater the score, the stronger the correlation.`}
>
<EuiIcon type={'iInCircle'} color="primary" />
</EuiToolTip>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<b>{correlationData?.score}</b>
<EuiText size="s">
<b>{correlationData?.score}</b>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<span>{timestamp}</span>
<EuiText size="s" color="subdued">
{timestamp}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
Expand Down
77 changes: 39 additions & 38 deletions public/pages/Correlations/containers/CorrelationsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ export class Correlations extends React.Component<CorrelationsProps, Correlation
maxWidth="400px"
key={findingCardsData.finding.id}
>
<EuiFlyoutHeader hasBorder>
<EuiFlyoutHeader>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="m">
Expand All @@ -462,11 +462,11 @@ export class Correlations extends React.Component<CorrelationsProps, Correlation
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiHorizontalRule margin="xs" />
<EuiTitle size="xs">
<p>Finding</p>
</EuiTitle>
<EuiSpacer size="xs" />
<FindingCard
id={findingCardsData.finding.id}
logType={findingCardsData.finding.logType}
Expand All @@ -475,7 +475,8 @@ export class Correlations extends React.Component<CorrelationsProps, Correlation
finding={findingCardsData.finding}
findings={findingCardsData.correlatedFindings}
/>
<EuiSpacer />
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiTitle size="xs">
<p>Correlated Findings ({findingCardsData.correlatedFindings.length})</p>
</EuiTitle>
Expand Down Expand Up @@ -508,45 +509,45 @@ export class Correlations extends React.Component<CorrelationsProps, Correlation
) : null}
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiTitle size="m">
<h1>Correlations</h1>
</EuiTitle>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="m">
<h1>Correlations</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSuperDatePicker
start={this.startTime}
end={this.endTime}
recentlyUsedRanges={this.state.recentlyUsedRanges}
onTimeChange={this.onTimeChange}
onRefresh={this.onRefresh}
updateButtonProps={{ fill: false }}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel>
<EuiFlexGroup wrap={true} justifyContent="spaceBetween">
<EuiFlexItem>
<EuiFlexGroup gutterSize="xs" wrap={false}>
<EuiFlexItem grow={false}>
<EuiFilterGroup>
<FilterGroup
groupName="Severity"
items={this.state.severityFilterOptions}
setItems={this.onSeverityFilterChange}
/>
<FilterGroup
groupName="Log types"
items={this.state.logTypeFilterOptions}
hasGroupOptions={true}
hasFooter={true}
setItems={this.onLogTypeFilterChange}
/>
</EuiFilterGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={this.resetFilters}>Reset filters</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup gutterSize="xs" wrap={false} justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiFilterGroup>
<FilterGroup
groupName="Log types"
items={this.state.logTypeFilterOptions}
hasGroupOptions={true}
hasFooter={true}
setItems={this.onLogTypeFilterChange}
/>
<FilterGroup
groupName="Severity"
items={this.state.severityFilterOptions}
setItems={this.onSeverityFilterChange}
/>
</EuiFilterGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSuperDatePicker
start={this.startTime}
end={this.endTime}
recentlyUsedRanges={this.state.recentlyUsedRanges}
onTimeChange={this.onTimeChange}
onRefresh={this.onRefresh}
updateButtonProps={{ fill: false }}
/>
<EuiButtonEmpty onClick={this.resetFilters}>Reset filters</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
Expand Down

0 comments on commit 9a63df0

Please sign in to comment.