Skip to content

Commit

Permalink
[Security Solution] Fix error messages on Rule Details page taking to…
Browse files Browse the repository at this point in the history
…o much space (elastic#157271)

## Summary

When a large "last execution" error message is displayed on Rule Details
page it takes too much vertical space:


https://user-images.githubusercontent.com/2946766/214444343-dde0d9cd-6bc7-4aca-a6cf-07891d206888.png

This PR changes the error callout component. Error text is now displayed
using EUI's TextBlock component. Users will see a scroll if error text
takes too much vertical space. Now it's also possible to view the text
fullscreen or copy it to clipboard.

<img width="1206" alt="Screenshot 2023-05-10 at 16 16 40"
src="https://github.com/elastic/kibana/assets/15949146/bca81852-afa0-44f6-acad-51c7c60c8a6f">

  ---

Another addition is a button to expand a row in the Execution Log to
view / copy a full error message.
<img width="1179" alt="Screenshot 2023-05-10 at 16 15 16"
src="https://github.com/elastic/kibana/assets/15949146/624e3fdc-8dd7-4fa1-a8a6-b1608be17eec">



### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)


### For maintainers

- [x] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)


**Known issue**: Copy button is not shown in Execution Log expandable
row in Safari. Opened an issue in EUI:
elastic/eui#6770
  • Loading branch information
nikitaindik authored and delanni committed May 25, 2023
1 parent 81500b2 commit a1ed2f1
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 37 deletions.
Expand Up @@ -6,8 +6,15 @@
*/

import React from 'react';
import { css } from '@emotion/react';
import type { EuiBasicTableColumn } from '@elastic/eui';
import { EuiLink, EuiText } from '@elastic/eui';
import {
EuiLink,
EuiText,
EuiButtonIcon,
EuiScreenReaderOnly,
RIGHT_ALIGNMENT,
} from '@elastic/eui';
import type { DocLinksStart } from '@kbn/core/public';
import { FormattedMessage } from '@kbn/i18n-react';

Expand All @@ -25,6 +32,37 @@ import { RuleDurationFormat } from './rule_duration_format';

import * as i18n from './translations';

type TableColumn = EuiBasicTableColumn<RuleExecutionResult>;

interface UseColumnsArgs {
toggleRowExpanded: (item: RuleExecutionResult) => void;
isRowExpanded: (item: RuleExecutionResult) => boolean;
}

export const expanderColumn = ({
toggleRowExpanded,
isRowExpanded,
}: UseColumnsArgs): TableColumn => {
return {
align: RIGHT_ALIGNMENT,
width: '40px',
isExpander: true,
name: (
<EuiScreenReaderOnly>
<span>{i18n.EXPAND_ROW}</span>
</EuiScreenReaderOnly>
),
render: (item: RuleExecutionResult) =>
item.security_status === 'succeeded' ? null : (
<EuiButtonIcon
onClick={() => toggleRowExpanded(item)}
aria-label={isRowExpanded(item) ? i18n.COLLAPSE : i18n.EXPAND}
iconType={isRowExpanded(item) ? 'arrowUp' : 'arrowDown'}
/>
),
};
};

export const EXECUTION_LOG_COLUMNS: Array<EuiBasicTableColumn<RuleExecutionResult>> = [
{
name: (
Expand Down Expand Up @@ -69,22 +107,39 @@ export const EXECUTION_LOG_COLUMNS: Array<EuiBasicTableColumn<RuleExecutionResul
truncateText: false,
width: '10%',
},
{
field: 'security_message',
name: (
<TableHeaderTooltipCell
title={i18n.COLUMN_MESSAGE}
tooltipContent={i18n.COLUMN_MESSAGE_TOOLTIP}
/>
),
render: (value: string) => <>{value}</>,
sortable: false,
truncateText: false,
width: '35%',
},
];

export const GET_EXECUTION_LOG_METRICS_COLUMNS = (
export const getMessageColumn = (width: string) => ({
field: 'security_message',
name: (
<TableHeaderTooltipCell
title={i18n.COLUMN_MESSAGE}
tooltipContent={i18n.COLUMN_MESSAGE_TOOLTIP}
/>
),
render: (value: string, record: RuleExecutionResult) => {
if (record.security_status === 'succeeded') {
return value;
}

return (
<div
css={css`
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
`}
>
{value}
</div>
);
},
sortable: false,
width,
});

export const getExecutionLogMetricsColumns = (
docLinks: DocLinksStart
): Array<EuiBasicTableColumn<RuleExecutionResult>> => [
{
Expand Down
Expand Up @@ -20,6 +20,7 @@ import {
EuiSwitch,
EuiBasicTable,
EuiButton,
EuiDescriptionList,
} from '@elastic/eui';

import type { Filter, Query } from '@kbn/es-query';
Expand Down Expand Up @@ -60,8 +61,15 @@ import { isAbsoluteTimeRange, isRelativeTimeRange } from '../../../../../common/
import { SourcererScopeName } from '../../../../../common/store/sourcerer/model';
import { useExecutionResults } from '../../../../rule_monitoring';
import { useRuleDetailsContext } from '../rule_details_context';
import { useExpandableRows } from '../../../../rule_monitoring/components/basic/tables/use_expandable_rows';
import { TextBlock } from '../../../../rule_monitoring/components/basic/text/text_block';
import * as i18n from './translations';
import { EXECUTION_LOG_COLUMNS, GET_EXECUTION_LOG_METRICS_COLUMNS } from './execution_log_columns';
import {
EXECUTION_LOG_COLUMNS,
getMessageColumn,
getExecutionLogMetricsColumns,
expanderColumn,
} from './execution_log_columns';
import { ExecutionLogSearchBar } from './execution_log_search_bar';

const EXECUTION_UUID_FIELD_NAME = 'kibana.alert.rule.execution.uuid';
Expand Down Expand Up @@ -361,7 +369,7 @@ const ExecutionLogTableComponent: React.FC<ExecutionLogTableProps> = ({
{
field: EXECUTION_UUID_FIELD_NAME,
name: i18n.COLUMN_ACTIONS,
width: '5%',
width: '64px',
actions: [
{
name: 'Edit',
Expand All @@ -386,14 +394,50 @@ const ExecutionLogTableComponent: React.FC<ExecutionLogTableProps> = ({
[onFilterByExecutionIdCallback]
);

const executionLogColumns = useMemo(
() =>
showMetricColumns
? [...EXECUTION_LOG_COLUMNS, ...GET_EXECUTION_LOG_METRICS_COLUMNS(docLinks), ...actions]
: [...EXECUTION_LOG_COLUMNS, ...actions],
[actions, docLinks, showMetricColumns]
const getItemId = useCallback((item: RuleExecutionResult): string => {
return `${item.execution_uuid}`;
}, []);

const renderExpandedItem = useCallback(
(item: RuleExecutionResult) => (
<EuiDescriptionList
className="eui-fullWidth"
listItems={[
{
title: i18n.ROW_DETAILS_MESSAGE,
description: <TextBlock text={item.security_message} />,
},
]}
/>
),
[]
);

const rows = useExpandableRows<RuleExecutionResult>({
getItemId,
renderItem: renderExpandedItem,
});

const executionLogColumns = useMemo(() => {
const columns = [...EXECUTION_LOG_COLUMNS];

if (showMetricColumns) {
columns.push(getMessageColumn('20%'), ...getExecutionLogMetricsColumns(docLinks));
} else {
columns.push(getMessageColumn('50%'));
}

columns.push(
...actions,
expanderColumn({
toggleRowExpanded: rows.toggleRowExpanded,
isRowExpanded: rows.isRowExpanded,
})
);

return columns;
}, [actions, docLinks, showMetricColumns, rows.toggleRowExpanded, rows.isRowExpanded]);

return (
<EuiPanel hasBorder>
{/* Filter bar */}
Expand Down Expand Up @@ -478,6 +522,9 @@ const ExecutionLogTableComponent: React.FC<ExecutionLogTableProps> = ({
sorting={sorting}
pagination={pagination}
onChange={onTableChangeCallback}
itemId={getItemId}
itemIdToExpandedRowMap={rows.itemIdToExpandedRowMap}
isExpandable={true}
/>
</EuiPanel>
);
Expand Down
Expand Up @@ -229,3 +229,31 @@ export const GREATER_THAN_YEAR = i18n.translate(
defaultMessage: '> 1 Year',
}
);

export const ROW_DETAILS_MESSAGE = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.fullMessage',
{
defaultMessage: 'Full message',
}
);

export const EXPAND_ROW = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.expandRow',
{
defaultMessage: 'Expand rows',
}
);

export const EXPAND = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.expand',
{
defaultMessage: 'Expand',
}
);

export const COLLAPSE = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.collapse',
{
defaultMessage: 'Collapse',
}
);
Expand Up @@ -7,7 +7,8 @@

import React from 'react';

import { EuiCallOut } from '@elastic/eui';
import { EuiCallOut, EuiCodeBlock } from '@elastic/eui';

import { FormattedDate } from '../../../../common/components/formatted_date';
import { RuleExecutionStatus } from '../../../../../common/detection_engine/rule_monitoring';

Expand All @@ -30,20 +31,36 @@ const RuleStatusFailedCallOutComponent: React.FC<RuleStatusFailedCallOutProps> =
}

return (
<EuiCallOut
title={
<>
{title} <FormattedDate value={date} fieldName="execution_summary.last_execution.date" />
</>
}
color={color}
iconType="warning"
data-test-subj="ruleStatusFailedCallOut"
<div
css={`
pre {
margin-block-end: 0;
margin-right: 24px; // Otherwise the copy button overlaps the scrollbar
padding-inline-end: 0;
}
`}
>
{message.split('\n').map((line) => (
<p>{line}</p>
))}
</EuiCallOut>
<EuiCallOut
title={
<>
{title} <FormattedDate value={date} fieldName="execution_summary.last_execution.date" />
</>
}
color={color}
iconType="warning"
data-test-subj="ruleStatusFailedCallOut"
>
<EuiCodeBlock
className="eui-fullWidth"
paddingSize="none"
isCopyable
overflowHeight={96}
transparentBackground
>
{message}
</EuiCodeBlock>
</EuiCallOut>
</div>
);
};

Expand Down

0 comments on commit a1ed2f1

Please sign in to comment.