Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution][Detections] Improves Table UI (consistent dates) #117643

1 change: 1 addition & 0 deletions x-pack/plugins/security_solution/common/constants.ts
Expand Up @@ -69,6 +69,7 @@ export const DEFAULT_RULE_REFRESH_IDLE_VALUE = 2700000 as const; // ms
export const DEFAULT_RULE_NOTIFICATION_QUERY_SIZE = 100 as const;
export const SECURITY_FEATURE_ID = 'Security' as const;
export const DEFAULT_SPACE_ID = 'default' as const;
export const DEFAULT_RELATIVE_DATE_THRESHOLD = 24 as const;

// Document path where threat indicator fields are expected. Fields are used
// to enrich signals, and are copied to threat.enrichments.
Expand Down
Expand Up @@ -166,5 +166,28 @@ describe('formatted_date', () => {

expect(wrapper.text()).toBe(getEmptyValue());
});

test('renders time as relative under 24hrs, configured through relativeThresholdInHrs', () => {
const timeThwentyThreeHrsAgo = new Date(
new Date().getTime() - 23 * 60 * 60 * 1000
).toISOString();
const wrapper = shallow(
<FormattedRelativePreferenceDate
relativeThresholdInHrs={24}
value={timeThwentyThreeHrsAgo}
/>
);

expect(wrapper.find('[data-test-subj="relative-time"]').exists()).toBe(true);
});

test('renders time as absolute over 24hrs, configured through relativeThresholdInHrs', () => {
const timeThirtyHrsAgo = new Date(new Date().getTime() - 30 * 60 * 60 * 1000).toISOString();
const wrapper = shallow(
<FormattedRelativePreferenceDate relativeThresholdInHrs={24} value={timeThirtyHrsAgo} />
);

expect(wrapper.find('[data-test-subj="preference-time"]').exists()).toBe(true);
});
});
});
Expand Up @@ -124,19 +124,22 @@ export interface FormattedRelativePreferenceDateProps {
* @see https://momentjs.com/docs/#/displaying/format/
*/
dateFormat?: string;
relativeThresholdInHrs?: number;
tooltipFieldName?: string;
tooltipAnchorClassName?: string;
}
/**
* Renders the specified date value according to under/over one hour
* Under an hour = relative format
* Over an hour = in a format determined by the user's preferences (can be overridden via prop),
* Renders the specified date value according to under/over configured by relativeThresholdInHrs in hours (default 1 hr)
* Under the relativeThresholdInHrs = relative format
* Over the relativeThresholdInHrs = in a format determined by the user's preferences (can be overridden via prop),
* with a tooltip that renders:
* - the name of the field
* - a humanized relative date (e.g. 16 minutes ago)
* - a long representation of the date that includes the day of the week (e.g. Thursday, March 21, 2019 6:47pm)
* - the raw date value (e.g. 2019-03-22T00:47:46Z)
*/
export const FormattedRelativePreferenceDate = React.memo<FormattedRelativePreferenceDateProps>(
({ value, dateFormat }) => {
({ value, dateFormat, tooltipFieldName, tooltipAnchorClassName, relativeThresholdInHrs = 1 }) => {
if (value == null) {
return getOrEmptyTagFromValue(value);
}
Expand All @@ -145,9 +148,17 @@ export const FormattedRelativePreferenceDate = React.memo<FormattedRelativePrefe
return getOrEmptyTagFromValue(value);
}
const date = maybeDate.toDate();
const shouldDisplayPreferenceTime = moment(date)
.add(relativeThresholdInHrs, 'hours')
.isBefore(new Date());

return (
<LocalizedDateTooltip date={date}>
{moment(date).add(1, 'hours').isBefore(new Date()) ? (
<LocalizedDateTooltip
date={date}
fieldName={tooltipFieldName}
className={tooltipAnchorClassName}
>
{shouldDisplayPreferenceTime ? (
<PreferenceFormattedDate
data-test-subj="preference-time"
value={date}
Expand Down
Expand Up @@ -14,15 +14,15 @@ import {
EuiIcon,
EuiLink,
} from '@elastic/eui';
import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react';
import { FormattedMessage } from '@kbn/i18n/react';
import * as H from 'history';
import { sum } from 'lodash';
import React, { Dispatch } from 'react';

import { isMlRule } from '../../../../../../common/machine_learning/helpers';
import { Rule, RuleStatus } from '../../../../containers/detection_engine/rules';
import { getEmptyTagValue } from '../../../../../common/components/empty_value';
import { FormattedDate } from '../../../../../common/components/formatted_date';
import { FormattedRelativePreferenceDate } from '../../../../../common/components/formatted_date';
import { getRuleDetailsUrl } from '../../../../../common/components/link_to/redirect_to_detection_engine';
import { ActionToaster } from '../../../../../common/components/toasters';
import { getStatusColor } from '../../../../components/rules/rule_status/helpers';
Expand All @@ -36,13 +36,16 @@ import {
exportRulesAction,
} from './actions';
import { RulesTableAction } from '../../../../containers/detection_engine/rules/rules_table';
import { LocalizedDateTooltip } from '../../../../../common/components/localized_date_tooltip';
import { LinkAnchor } from '../../../../../common/components/links';
import { getToolTipContent, canEditRuleWithActions } from '../../../../../common/utils/privileges';
import { PopoverTooltip } from './popover_tooltip';
import { TagsDisplay } from './tag_display';
import { getRuleStatusText } from '../../../../../../common/detection_engine/utils';
import { APP_UI_ID, SecurityPageName } from '../../../../../../common/constants';
import {
APP_UI_ID,
SecurityPageName,
DEFAULT_RELATIVE_DATE_THRESHOLD,
} from '../../../../../../common/constants';
import { DocLinksStart, NavigateToAppOptions } from '../../../../../../../../../src/core/public';

export const getActions = (
Expand Down Expand Up @@ -200,9 +203,12 @@ export const getColumns = ({
return value == null ? (
getEmptyTagValue()
) : (
<LocalizedDateTooltip fieldName={i18n.COLUMN_LAST_COMPLETE_RUN} date={new Date(value)}>
<FormattedRelative value={value} />
</LocalizedDateTooltip>
<FormattedRelativePreferenceDate
tooltipFieldName={i18n.COLUMN_LAST_COMPLETE_RUN}
relativeThresholdInHrs={DEFAULT_RELATIVE_DATE_THRESHOLD}
value={value}
tooltipAnchorClassName="eui-textTruncate"
/>
);
},
width: '14%',
Expand All @@ -228,9 +234,12 @@ export const getColumns = ({
return value == null ? (
getEmptyTagValue()
) : (
<LocalizedDateTooltip fieldName={i18n.COLUMN_LAST_UPDATE} date={new Date(value)}>
<FormattedDate value={value} fieldName={'last rule update date'} />
</LocalizedDateTooltip>
<FormattedRelativePreferenceDate
tooltipFieldName={i18n.COLUMN_LAST_UPDATE}
relativeThresholdInHrs={DEFAULT_RELATIVE_DATE_THRESHOLD}
value={value}
tooltipAnchorClassName="eui-textTruncate"
/>
);
},
sortable: true,
Expand Down Expand Up @@ -410,9 +419,12 @@ export const getMonitoringColumns = (
return value == null ? (
getEmptyTagValue()
) : (
<LocalizedDateTooltip fieldName={i18n.COLUMN_LAST_COMPLETE_RUN} date={new Date(value)}>
<FormattedRelative value={value} />
</LocalizedDateTooltip>
<FormattedRelativePreferenceDate
tooltipFieldName={i18n.COLUMN_LAST_COMPLETE_RUN}
relativeThresholdInHrs={DEFAULT_RELATIVE_DATE_THRESHOLD}
value={value}
tooltipAnchorClassName="eui-textTruncate"
/>
);
},
width: '20%',
Expand Down
Expand Up @@ -9,7 +9,10 @@ import React from 'react';
import { EuiButtonIcon, EuiBasicTableColumn, EuiToolTip } from '@elastic/eui';

import type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types';
import { DEFAULT_RELATIVE_DATE_THRESHOLD } from '../../../../../../../common/constants';
import { FormatUrl } from '../../../../../../common/components/link_to';
import { FormattedRelativePreferenceDate } from '../../../../../../common/components/formatted_date';

import * as i18n from './translations';
import { ExceptionListInfo } from './use_all_exception_lists';
import { ExceptionOverflowDisplay } from './exceptions_overflow_display';
Expand Down Expand Up @@ -84,13 +87,29 @@ export const getAllExceptionListsColumns = (
truncateText: true,
dataType: 'date',
width: '14%',
render: (value: ExceptionListInfo['created_at']) => (
<FormattedRelativePreferenceDate
relativeThresholdInHrs={DEFAULT_RELATIVE_DATE_THRESHOLD}
value={value}
tooltipFieldName={i18n.LIST_DATE_CREATED_TITLE}
tooltipAnchorClassName="eui-textTruncate"
/>
),
},
{
align: 'left',
field: 'updated_at',
name: i18n.LIST_DATE_UPDATED_TITLE,
truncateText: true,
width: '14%',
render: (value: ExceptionListInfo['updated_at']) => (
<FormattedRelativePreferenceDate
relativeThresholdInHrs={DEFAULT_RELATIVE_DATE_THRESHOLD}
value={value}
tooltipFieldName={i18n.LIST_DATE_UPDATED_TITLE}
tooltipAnchorClassName="eui-textTruncate"
/>
),
},
{
align: 'center',
Expand Down