[SIEM] table tweaks (#42195) (#42413)

* pagination media query, rm extraneous elms, clean

* make tooltip icons consistent

* remove unnecessary icon headers

* timeline table cleanup

* update tests

* correct translation casing and cleanup

* update translations

* update tests and snapshots

* update casing

* fix console warning

* restore missing translations

* update snapshot

* update casing
This commit is contained in:
Michael Marcialis 2019-07-31 23:07:57 -04:00 committed by GitHub
parent 8622e7c8b2
commit 51f71683f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 1591 additions and 1663 deletions

View file

@ -17,7 +17,7 @@ export const CATEGORIES = i18n.translate('xpack.siem.fieldBrowser.categoriesTitl
export const CATEGORIES_COUNT = (totalCount: number) =>
i18n.translate('xpack.siem.fieldBrowser.categoriesCountTitle', {
values: { totalCount },
defaultMessage: '{totalCount} {totalCount, plural, =1 {Category} other {Categories}}',
defaultMessage: '{totalCount} {totalCount, plural, =1 {category} other {categories}}',
});
export const COPY_TO_CLIPBOARD = i18n.translate('xpack.siem.fieldBrowser.copyToClipboard', {
@ -43,7 +43,7 @@ export const FIELDS = i18n.translate('xpack.siem.fieldBrowser.fieldsTitle', {
export const FIELDS_COUNT = (totalCount: number) =>
i18n.translate('xpack.siem.fieldBrowser.fieldsCountTitle', {
values: { totalCount },
defaultMessage: '{totalCount} {totalCount, plural, =1 {Field} other {Fields}}',
defaultMessage: '{totalCount} {totalCount, plural, =1 {field} other {fields}}',
});
export const FILTER_PLACEHOLDER = i18n.translate('xpack.siem.fieldBrowser.filterPlaceholder', {

View file

@ -38,25 +38,27 @@ export const HeaderPanel = pure<HeaderPanelProps>(
<Header border={border}>
<EuiFlexGroup alignItems="center" gutterSize="m">
<EuiFlexItem>
<EuiFlexGroup alignItems="center" gutterSize="none">
<EuiFlexItem grow={false}>
<EuiTitle>
<h2 data-test-subj="page_headline_title">{title}</h2>
</EuiTitle>
</EuiFlexItem>
{tooltip && (
<EuiFlexItem grow={false}>
<EuiIconTip color="subdued" content={tooltip} position="top" size="l" />
</EuiFlexItem>
)}
</EuiFlexGroup>
<EuiTitle>
<h2 data-test-subj="panel_headline_title">
{title}
{tooltip && (
<>
{' '}
<EuiIconTip color="subdued" content={tooltip} size="l" type="iInCircle" />
</>
)}
</h2>
</EuiTitle>
<EuiText color="subdued" size="s">
{subtitle}
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{id && <InspectButton queryId={id} inspectIndex={0} show={showInspect} title={title} />}
</EuiFlexItem>
{children && <EuiFlexItem grow={false}>{children}</EuiFlexItem>}
</EuiFlexGroup>
</Header>

View file

@ -4,8 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { mount } from 'enzyme';
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import {
TestProviderWithoutDragAndDrop,
@ -19,6 +21,7 @@ import { InspectButton } from '.';
import { cloneDeep } from 'lodash/fp';
describe('Inspect Button', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
const refetch = jest.fn();
const state: State = mockGlobalState;
const newQuery: UpdateQueryParams = {
@ -98,9 +101,11 @@ describe('Inspect Button', () => {
});
test('Open Inspect Modal', () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop store={store}>
<InspectButton queryId={newQuery.id} show={true} title="My title" />
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop store={store}>
<InspectButton queryId={newQuery.id} show={true} title="My title" />
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
wrapper
.find('button[data-test-subj="inspect-icon-button"]')
@ -120,9 +125,11 @@ describe('Inspect Button', () => {
test('Close Inspect Modal', () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop store={store}>
<InspectButton queryId={newQuery.id} show={true} title="My title" />
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop store={store}>
<InspectButton queryId={newQuery.id} show={true} title="My title" />
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
wrapper
.find('button[data-test-subj="inspect-icon-button"]')

View file

@ -4,8 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { mount } from 'enzyme';
import React from 'react';
import { ThemeProvider } from 'styled-components';
import { ModalInspectQuery } from './modal';
@ -15,17 +17,21 @@ const response =
'{"took": 880,"timed_out": false,"_shards": {"total": 26,"successful": 26,"skipped": 0,"failed": 0},"hits": {"max_score": null,"hits": []},"aggregations": {"hosts": {"value": 541},"hosts_histogram": {"buckets": [{"key_as_string": "2019 - 07 - 05T01: 00: 00.000Z", "key": 1562288400000, "doc_count": 1492321, "count": { "value": 105 }}, {"key_as_string": "2019 - 07 - 05T13: 00: 00.000Z", "key": 1562331600000, "doc_count": 2412761, "count": { "value": 453}},{"key_as_string": "2019 - 07 - 06T01: 00: 00.000Z", "key": 1562374800000, "doc_count": 111658, "count": { "value": 15}}],"interval": "12h"}},"status": 200}';
describe('Modal Inspect', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
const closeModal = jest.fn();
describe('rendering', () => {
test('when isShowing is positive and request and response are not null', () => {
const wrapper = mount(
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
<ThemeProvider theme={theme}>
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
</ThemeProvider>
);
expect(
wrapper
@ -99,13 +105,15 @@ describe('Modal Inspect', () => {
describe('functionality from tab statistics/request/response', () => {
test('Click on statistic Tab', () => {
const wrapper = mount(
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
<ThemeProvider theme={theme}>
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
</ThemeProvider>
);
wrapper
@ -116,7 +124,7 @@ describe('Modal Inspect', () => {
expect(
wrapper.find('.euiDescriptionList__title span[data-test-subj="index-pattern-title"]').text()
).toBe('Index pattern');
).toBe('Index pattern ');
expect(
wrapper
.find('.euiDescriptionList__description span[data-test-subj="index-pattern-description"]')
@ -124,7 +132,7 @@ describe('Modal Inspect', () => {
).toBe('auditbeat-*, filebeat-*, packetbeat-*, winlogbeat-*');
expect(
wrapper.find('.euiDescriptionList__title span[data-test-subj="query-time-title"]').text()
).toBe('Query time');
).toBe('Query time ');
expect(
wrapper
.find('.euiDescriptionList__description span[data-test-subj="query-time-description"]')
@ -134,18 +142,20 @@ describe('Modal Inspect', () => {
wrapper
.find('.euiDescriptionList__title span[data-test-subj="request-timestamp-title"]')
.text()
).toBe('Request timestamp');
).toBe('Request timestamp ');
});
test('Click on request Tab', () => {
const wrapper = mount(
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
<ThemeProvider theme={theme}>
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
</ThemeProvider>
);
wrapper
@ -214,13 +224,15 @@ describe('Modal Inspect', () => {
test('Click on response Tab', () => {
const wrapper = mount(
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
<ThemeProvider theme={theme}>
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
</ThemeProvider>
);
wrapper
@ -258,13 +270,15 @@ describe('Modal Inspect', () => {
describe('events', () => {
test('Make sure that toggle function has been called when you click on the close button', () => {
const wrapper = mount(
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
<ThemeProvider theme={theme}>
<ModalInspectQuery
closeModal={closeModal}
isShowing={true}
request={request}
response={response}
title="My title"
/>
</ThemeProvider>
);
wrapper.find('button[data-test-subj="modal-inspect-close"]').simulate('click');

View file

@ -25,14 +25,14 @@ import styled from 'styled-components';
import * as i18n from './translations';
const DescriptionListStyled = styled(EuiDescriptionList)`
dt .euiToolTipAnchor {
padding-left: 5px;
}
dt.euiDescriptionList__title {
width: 30% !important;
}
dd.euiDescriptionList__description {
width: 70% !important;
@media only screen and (min-width: ${props => props.theme.eui.euiBreakpoints.s}) {
.euiDescriptionList__title {
width: 30% !important;
}
.euiDescriptionList__description {
width: 70% !important;
}
}
`;
@ -103,8 +103,8 @@ export const ModalInspectQuery = ({
{
title: (
<span data-test-subj="index-pattern-title">
{i18n.INDEX_PATTERN}
<EuiIconTip content={i18n.INDEX_PATTERN_DESC} position="top" type="iInCircle" />
{i18n.INDEX_PATTERN}{' '}
<EuiIconTip color="subdued" content={i18n.INDEX_PATTERN_DESC} type="iInCircle" />
</span>
),
description: (
@ -117,8 +117,8 @@ export const ModalInspectQuery = ({
{
title: (
<span data-test-subj="query-time-title">
{i18n.QUERY_TIME}
<EuiIconTip content={i18n.QUERY_TIME_DESC} position="top" type="iInCircle" />
{i18n.QUERY_TIME}{' '}
<EuiIconTip color="subdued" content={i18n.QUERY_TIME_DESC} type="iInCircle" />
</span>
),
description: (
@ -132,8 +132,8 @@ export const ModalInspectQuery = ({
{
title: (
<span data-test-subj="request-timestamp-title">
{i18n.REQUEST_TIMESTAMP}
<EuiIconTip content={i18n.REQUEST_TIMESTAMP_DESC} position="top" type="iInCircle" />
{i18n.REQUEST_TIMESTAMP}{' '}
<EuiIconTip color="subdued" content={i18n.REQUEST_TIMESTAMP_DESC} type="iInCircle" />
</span>
),
description: (

View file

@ -63,7 +63,7 @@ describe('Last Event Time Stat', () => {
await wait();
expect(container.innerHTML).toBe(
'<span class="euiToolTipAnchor">Last Event: 12 days ago</span>'
'<span class="euiToolTipAnchor">Last event: 12 days ago</span>'
);
});
test('Bad date time string', async () => {

View file

@ -51,7 +51,7 @@ export const LastEventTime = pure<LastEventTimeProps>(({ hostName, indexKey, ip
<EuiToolTip data-test-subj="last_event_time" position="bottom" content={lastSeen}>
<FormattedMessage
id="xpack.siem.headerPage.pageSubtitle"
defaultMessage="Last Event: {beat}"
defaultMessage="Last event: {beat}"
values={{
beat: <FormattedRelative value={new Date(lastSeen)} />,
}}

View file

@ -226,36 +226,34 @@ export class LoadMoreTable<T, U, V, W, X, Y, Z, AA, AB> extends React.PureCompon
{hasNextPage && (
<FooterAction>
<EuiFlexGroup alignItems="flexStart">
<EuiFlexItem>
{!isEmpty(itemsPerRow) && (
<EuiPopover
id="customizablePagination"
data-test-subj="loadingMoreSizeRowPopover"
button={button}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover}
panelPaddingSize="none"
>
<EuiContextMenuPanel
items={rowItems}
data-test-subj="loadingMorePickSizeRow"
/>
</EuiPopover>
)}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="loadingMoreButton"
isLoading={loading}
onClick={this.props.loadMore}
size="s"
<EuiFlexItem>
{!isEmpty(itemsPerRow) && (
<EuiPopover
id="customizablePagination"
data-test-subj="loadingMoreSizeRowPopover"
button={button}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover}
panelPaddingSize="none"
>
{loading ? `${i18n.LOADING}` : i18n.LOAD_MORE}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
<EuiContextMenuPanel
items={rowItems}
data-test-subj="loadingMorePickSizeRow"
/>
</EuiPopover>
)}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="loadingMoreButton"
isLoading={loading}
onClick={this.props.loadMore}
size="s"
>
{loading ? `${i18n.LOADING}` : i18n.LOAD_MORE}
</EuiButton>
</EuiFlexItem>
</FooterAction>
)}
@ -314,6 +312,9 @@ const BasicTable = styled(EuiBasicTable)`
}
`;
const FooterAction = styled.div`
margin-top: ${props => props.theme.eui.euiSize};
const FooterAction = styled(EuiFlexGroup).attrs({
alignItems: 'center',
responsive: false,
})`
margin-top: ${props => props.theme.eui.euiSizeXS};
`;

View file

@ -65,9 +65,9 @@ export const AnomaliesHostTable = React.memo<AnomaliesHostTableProps>(
return (
<Panel loading={{ loading }}>
<HeaderPanel
subtitle={`${i18n.SHOWING}: ${pagination.totalItemCount.toLocaleString()} ${
i18n.ANOMALIES
}`}
subtitle={`${i18n.SHOWING}: ${pagination.totalItemCount.toLocaleString()} ${i18n.UNIT(
pagination.totalItemCount
)}`}
title={i18n.ANOMALIES}
tooltip={i18n.TOOLTIP}
/>

View file

@ -63,9 +63,9 @@ export const AnomaliesNetworkTable = React.memo<AnomaliesNetworkTableProps>(
return (
<Panel loading={{ loading }}>
<HeaderPanel
subtitle={`${i18n.SHOWING}: ${pagination.totalItemCount.toLocaleString()} ${
i18n.ANOMALIES
}`}
subtitle={`${i18n.SHOWING}: ${pagination.totalItemCount.toLocaleString()} ${i18n.UNIT(
pagination.totalItemCount
)}`}
title={i18n.ANOMALIES}
tooltip={i18n.TOOLTIP}
/>

View file

@ -14,24 +14,26 @@ export const ANOMALIES = i18n.translate('xpack.siem.anomaliesTable.table.anomali
defaultMessage: 'Anomalies',
});
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.anomaliesTable.table.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {anomaly} other {anomalies}}`,
});
export const TOOLTIP = i18n.translate('xpack.siem.anomaliesTable.table.anomaliesTooltip', {
defaultMessage: 'The anomalies table is not filterable via the SIEM global KQL search.',
});
export const LOADING = i18n.translate('xpack.siem.ml.table.loadingDescription', {
defaultMessage: 'Loading…',
});
export const SCORE = i18n.translate('xpack.siem.ml.table.scoreTitle', {
defaultMessage: 'Anomaly Score',
defaultMessage: 'Anomaly score',
});
export const HOST_NAME = i18n.translate('xpack.siem.ml.table.hostNameTitle', {
defaultMessage: 'Host Name',
defaultMessage: 'Host name',
});
export const INFLUENCED_BY = i18n.translate('xpack.siem.ml.table.influencedByTitle', {
defaultMessage: 'Influenced By',
defaultMessage: 'Influenced by',
});
export const ENTITY = i18n.translate('xpack.siem.ml.table.entityTitle', {
@ -39,7 +41,7 @@ export const ENTITY = i18n.translate('xpack.siem.ml.table.entityTitle', {
});
export const DETECTOR = i18n.translate('xpack.siem.ml.table.detectorTitle', {
defaultMessage: 'Job Name',
defaultMessage: 'Job name',
});
export const NETWORK_NAME = i18n.translate('xpack.siem.ml.table.networkNameTitle', {

View file

@ -23,7 +23,7 @@ exports[`FilterGroup renders correctly against snapshot 1`] = `
target="_blank"
type="button"
>
Subscription Options
Subscription options
</EuiButton>
</styled.div>
`;

View file

@ -33,7 +33,7 @@ exports[`FilterGroup renders correctly against snapshot 1`] = `
type="button"
withNext={true}
>
Elastic Jobs
Elastic jobs
</EuiFilterButton>
<EuiFilterButton
color="text"
@ -44,7 +44,7 @@ exports[`FilterGroup renders correctly against snapshot 1`] = `
onClick={[Function]}
type="button"
>
Custom Jobs
Custom jobs
</EuiFilterButton>
</EuiFilterGroup>
</EuiFlexItem>

View file

@ -5,12 +5,12 @@ exports[`JobsTable renders correctly against snapshot 1`] = `
columns={
Array [
Object {
"name": "Job Name",
"name": "Job name",
"render": [Function],
},
Object {
"align": "center",
"name": "Run Job",
"name": "Run job",
"render": [Function],
"width": "80px",
},

View file

@ -10,7 +10,7 @@ exports[`ShowingCount renders correctly against snapshot 1`] = `
>
<FormattedMessage
data-test-subj="query-message"
defaultMessage="Showing: {filterResultsLength} {filterResultsLength, plural, one {Job} other {Jobs}}"
defaultMessage="Showing: {filterResultsLength} {filterResultsLength, plural, one {job} other {jobs}}"
id="xpack.siem.components.mlPopup.showingLabel"
values={
Object {

View file

@ -25,7 +25,7 @@ export const ShowingCount = React.memo<ShowingCountProps>(({ filterResultsLength
<FormattedMessage
data-test-subj="query-message"
id="xpack.siem.components.mlPopup.showingLabel"
defaultMessage="Showing: {filterResultsLength} {filterResultsLength, plural, one {Job} other {Jobs}}"
defaultMessage="Showing: {filterResultsLength} {filterResultsLength, plural, one {job} other {jobs}}"
values={{
filterResultsLength,
}}

View file

@ -16,7 +16,7 @@ export const ANOMALY_DETECTION = i18n.translate(
export const ANOMALY_DETECTION_TITLE = i18n.translate(
'xpack.siem.components.mlPopup.anomalyDetectionTitle',
{
defaultMessage: 'Anomaly Detection Settings',
defaultMessage: 'Anomaly detection settings',
}
);
@ -33,7 +33,7 @@ export const UPGRADE_DESCRIPTION = i18n.translate(
);
export const UPGRADE_BUTTON = i18n.translate('xpack.siem.components.mlPopup.upgradeButtonLabel', {
defaultMessage: 'Subscription Options',
defaultMessage: 'Subscription options',
});
export const FILTER_PLACEHOLDER = i18n.translate(
@ -44,31 +44,31 @@ export const FILTER_PLACEHOLDER = i18n.translate(
);
export const SHOW_ELASTIC_JOBS = i18n.translate('xpack.siem.components.mlPopup.showAllJobsLabel', {
defaultMessage: 'Elastic Jobs',
defaultMessage: 'Elastic jobs',
});
export const SHOW_CUSTOM_JOBS = i18n.translate('xpack.siem.components.mlPopup.showSiemJobsLabel', {
defaultMessage: 'Custom Jobs',
defaultMessage: 'Custom jobs',
});
export const COLUMN_JOB_NAME = i18n.translate(
'xpack.siem.components.mlPopup.jobsTable.jobNameColumn',
{
defaultMessage: 'Job Name',
defaultMessage: 'Job name',
}
);
export const COLUMN_RUN_JOB = i18n.translate(
'xpack.siem.components.mlPopup.jobsTable.runJobColumn',
{
defaultMessage: 'Run Job',
defaultMessage: 'Run job',
}
);
export const NO_ITEMS_TEXT = i18n.translate(
'xpack.siem.components.mlPopup.jobsTable.noItemsDescription',
{
defaultMessage: 'No SIEM Machine Learning Jobs Found',
defaultMessage: 'No SIEM Machine Learning jobs found',
}
);

View file

@ -26,7 +26,7 @@ describe('DeleteTimelineModal', () => {
.find('[data-test-subj="title"]')
.first()
.text()
).toEqual('Delete `Privilege Escalation`?');
).toEqual('Delete "Privilege Escalation"?');
});
test('it trims leading and trailing whitespace around the title', () => {
@ -43,7 +43,7 @@ describe('DeleteTimelineModal', () => {
.find('[data-test-subj="title"]')
.first()
.text()
).toEqual('Delete `Leading and trailing whitespace`?');
).toEqual('Delete "Leading and trailing whitespace"?');
});
test('it displays `Untitled Timeline` in the title when title is undefined', () => {
@ -56,7 +56,7 @@ describe('DeleteTimelineModal', () => {
.find('[data-test-subj="title"]')
.first()
.text()
).toEqual('Delete `Untitled Timeline`?');
).toEqual('Delete "Untitled timeline"?');
});
test('it displays `Untitled Timeline` in the title when title is null', () => {
@ -69,7 +69,7 @@ describe('DeleteTimelineModal', () => {
.find('[data-test-subj="title"]')
.first()
.text()
).toEqual('Delete `Untitled Timeline`?');
).toEqual('Delete "Untitled timeline"?');
});
test('it displays `Untitled Timeline` in the title when title is just whitespace', () => {
@ -82,7 +82,7 @@ describe('DeleteTimelineModal', () => {
.find('[data-test-subj="title"]')
.first()
.text()
).toEqual('Delete `Untitled Timeline`?');
).toEqual('Delete "Untitled timeline"?');
});
test('it renders a deletion warning', () => {

View file

@ -28,7 +28,7 @@ export const DeleteTimelineModal = pure<Props>(({ title, toggleShowModal, onDele
<FormattedMessage
id="xpack.siem.open.timeline.deleteTimelineModalTitle"
data-test-subj="title"
defaultMessage="Delete `{title}`?"
defaultMessage='Delete "{title}"?'
values={{
title: title != null && title.trim().length > 0 ? title.trim() : i18n.UNTITLED_TIMELINE,
}}

View file

@ -7,10 +7,9 @@
import { EuiButtonIcon, EuiModal, EuiToolTip, EuiOverlayMask } from '@elastic/eui';
import * as React from 'react';
import { DeleteTimelines } from '../types';
import { DeleteTimelineModal, DELETE_TIMELINE_MODAL_WIDTH } from './delete_timeline_modal';
import * as i18n from '../translations';
import { DeleteTimelines } from '../types';
interface Props {
deleteTimelines?: DeleteTimelines;
@ -40,7 +39,7 @@ export class DeleteTimelineModalButton extends React.PureComponent<Props, State>
<EuiToolTip content={i18n.DELETE}>
<EuiButtonIcon
aria-label={i18n.DELETE}
color="subdued"
color="danger"
data-test-subj="delete-timeline"
iconSize="s"
iconType="trash"

View file

@ -4,10 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { mount, ReactWrapper } from 'enzyme';
import { get } from 'lodash/fp';
import { MockedProvider } from 'react-apollo/test-utils';
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import { wait } from '../../lib/helpers';
import { TestProviderWithoutDragAndDrop, apolloClient } from '../../mock/test_providers';
@ -32,23 +34,27 @@ React.Component<{}, {}, any> =>
.childAt(0)
.childAt(0)
.childAt(0)
.childAt(0)
.instance();
describe('StatefulOpenTimeline', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
const title = 'All Timelines / Open Timelines';
test('it has the expected initial state', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -69,16 +75,18 @@ describe('StatefulOpenTimeline', () => {
describe('#onQueryChange', () => {
test('it updates the query state with the expected trimmed value when the user enters a query', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -104,16 +112,18 @@ describe('StatefulOpenTimeline', () => {
test('it appends the word "with" to the Showing in Timelines message when the user enters a query', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -129,21 +139,23 @@ describe('StatefulOpenTimeline', () => {
.find('[data-test-subj="query-message"]')
.first()
.text()
).toContain('Showing 11 Timelines with');
).toContain('Showing: 11 timelines with');
});
test('echos (renders) the query when the user enters a query', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -159,23 +171,25 @@ describe('StatefulOpenTimeline', () => {
.find('[data-test-subj="selectable-query-text"]')
.first()
.text()
).toEqual('abcd');
).toEqual('with "abcd"');
});
});
describe('#focusInput', () => {
test('focuses the input when the component mounts', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -195,16 +209,18 @@ describe('StatefulOpenTimeline', () => {
const addTimelinesToFavorites = jest.fn();
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -240,16 +256,18 @@ describe('StatefulOpenTimeline', () => {
const deleteTimelines = jest.fn();
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -282,16 +300,18 @@ describe('StatefulOpenTimeline', () => {
describe('#onSelectionChange', () => {
test('it updates the selection state when timelines are selected', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -310,16 +330,18 @@ describe('StatefulOpenTimeline', () => {
describe('#onTableChange', () => {
test('it updates the sort state when the user clicks on a column to sort it', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -349,16 +371,18 @@ describe('StatefulOpenTimeline', () => {
describe('#onToggleOnlyFavorites', () => {
test('it updates the onlyFavorites state when the user clicks the Only Favorites button', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -386,16 +410,18 @@ describe('StatefulOpenTimeline', () => {
describe('#onToggleShowNotes', () => {
test('it updates the itemIdToExpandedNotesRowMap state when the user clicks the expand notes button', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -436,16 +462,18 @@ describe('StatefulOpenTimeline', () => {
test('it renders the expanded notes when the expand button is clicked', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -471,31 +499,7 @@ describe('StatefulOpenTimeline', () => {
test('it renders the title', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
);
await wait();
expect(
wrapper
.find('[data-test-subj="title"]')
.first()
.text()
).toEqual(title);
});
describe('#resetSelectionState', () => {
test('when the user deletes selected timelines, resetSelectionState is invoked to clear the selection state', async () => {
const wrapper = mount(
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
@ -506,6 +510,34 @@ describe('StatefulOpenTimeline', () => {
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
expect(
wrapper
.find('[data-test-subj="panel_headline_title"]')
.first()
.text()
).toEqual(title);
});
describe('#resetSelectionState', () => {
test('when the user deletes selected timelines, resetSelectionState is invoked to clear the selection state', async () => {
const wrapper = mount(
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -531,16 +563,18 @@ describe('StatefulOpenTimeline', () => {
test('it renders the expected count of matching timelines when no query has been entered', async () => {
const wrapper = mount(
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<TestProviderWithoutDragAndDrop>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</TestProviderWithoutDragAndDrop>
</MockedProvider>
<ThemeProvider theme={theme}>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<TestProviderWithoutDragAndDrop>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</TestProviderWithoutDragAndDrop>
</MockedProvider>
</ThemeProvider>
);
await wait();
@ -552,7 +586,7 @@ describe('StatefulOpenTimeline', () => {
.find('[data-test-subj="query-message"]')
.first()
.text()
).toContain('Showing 11 Timelines ');
).toContain('Showing: 11 timelines ');
});
// TODO - Have been skip because we need to re-implement the test as the component changed
@ -560,16 +594,18 @@ describe('StatefulOpenTimeline', () => {
const onOpenTimeline = jest.fn();
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -595,16 +631,18 @@ describe('StatefulOpenTimeline', () => {
const onOpenTimeline = jest.fn();
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<StatefulOpenTimeline
apolloClient={apolloClient}
isModal={false}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
title={title}
/>
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();

View file

@ -4,16 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { cloneDeep } from 'lodash/fp';
import moment from 'moment';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import { mockTimelineResults } from '../../../mock/timeline_results';
import { OpenTimelineResult, TimelineResultNote } from '../types';
import { NotePreviews } from '.';
describe('NotePreviews', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
let mockResults: OpenTimelineResult[];
let note1updated: number;
let note2updated: number;
@ -33,7 +36,11 @@ describe('NotePreviews', () => {
test('it renders a note preview for each note when isModal is false', () => {
const hasNotes: OpenTimelineResult[] = [{ ...mockResults[0] }];
const wrapper = mountWithIntl(<NotePreviews isModal={false} notes={hasNotes[0].notes} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreviews isModal={false} notes={hasNotes[0].notes} />
</ThemeProvider>
);
hasNotes[0].notes!.forEach(({ savedObjectId }) => {
expect(wrapper.find(`[data-test-subj="note-preview-${savedObjectId}"]`).exists()).toBe(true);
@ -43,7 +50,11 @@ describe('NotePreviews', () => {
test('it renders a note preview for each note when isModal is true', () => {
const hasNotes: OpenTimelineResult[] = [{ ...mockResults[0] }];
const wrapper = mountWithIntl(<NotePreviews isModal={true} notes={hasNotes[0].notes} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreviews isModal={true} notes={hasNotes[0].notes} />
</ThemeProvider>
);
hasNotes[0].notes!.forEach(({ savedObjectId }) => {
expect(wrapper.find(`[data-test-subj="note-preview-${savedObjectId}"]`).exists()).toBe(true);
@ -90,7 +101,11 @@ describe('NotePreviews', () => {
},
];
const wrapper = mountWithIntl(<NotePreviews isModal={false} notes={nonUniqueNotes} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreviews isModal={false} notes={nonUniqueNotes} />
</ThemeProvider>
);
expect(
wrapper
@ -122,7 +137,11 @@ describe('NotePreviews', () => {
},
];
const wrapper = mountWithIntl(<NotePreviews isModal={false} notes={nonUniqueNotes} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreviews isModal={false} notes={nonUniqueNotes} />
</ThemeProvider>
);
expect(
wrapper
@ -153,7 +172,11 @@ describe('NotePreviews', () => {
},
];
const wrapper = mountWithIntl(<NotePreviews isModal={false} notes={nonUniqueNotes} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreviews isModal={false} notes={nonUniqueNotes} />
</ThemeProvider>
);
expect(
wrapper

View file

@ -12,16 +12,11 @@ import styled from 'styled-components';
import { NotePreview } from './note_preview';
import { TimelineResultNote } from '../types';
const NotePreviewsContainer = styled.div<{ paddingLeft: number }>`
padding-left: ${({ paddingLeft }) => `${paddingLeft}px`};
const NotePreviewsContainer = styled.section`
padding: ${props =>
`${props.theme.eui.euiSizeS} 0 ${props.theme.eui.euiSizeS} ${props.theme.eui.euiSizeXXL}`};
`;
/** The default left-padding of a notes preview */
const DEFAULT_NOTES_PREVIEW_LEFT_PADDING = 28; // px
/** The left padding of a notes preview in a modal */
const MODAL_NOTES_PREVIEW_LEFT_PADDING = 31; // px
/**
* Renders a preview of a note in the All / Open Timelines table
*/
@ -36,10 +31,7 @@ export const NotePreviews = pure<{
const uniqueNotes = uniqBy('savedObjectId', notes);
return (
<NotePreviewsContainer
data-test-subj="note-previews-container"
paddingLeft={isModal ? MODAL_NOTES_PREVIEW_LEFT_PADDING : DEFAULT_NOTES_PREVIEW_LEFT_PADDING}
>
<NotePreviewsContainer data-test-subj="note-previews-container">
{uniqueNotes.map(({ note, savedObjectId, updated, updatedBy }) =>
savedObjectId != null ? (
<NotePreview

View file

@ -4,8 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import { getEmptyValue } from '../../empty_value';
import { NotePreview } from './note_preview';
@ -13,9 +15,15 @@ import { NotePreview } from './note_preview';
import * as i18n from '../translations';
describe('NotePreview', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
describe('Avatar', () => {
test('it renders an avatar with the expected initials when updatedBy is provided', () => {
const wrapper = mountWithIntl(<NotePreview updatedBy="admin" />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview updatedBy="admin" />
</ThemeProvider>
);
expect(
wrapper
@ -26,7 +34,11 @@ describe('NotePreview', () => {
});
test('it renders an avatar with a "?" when updatedBy is undefined', () => {
const wrapper = mountWithIntl(<NotePreview />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview />
</ThemeProvider>
);
expect(
wrapper
@ -37,7 +49,11 @@ describe('NotePreview', () => {
});
test('it renders an avatar with a "?" when updatedBy is null', () => {
const wrapper = mountWithIntl(<NotePreview updatedBy={null} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview updatedBy={null} />
</ThemeProvider>
);
expect(
wrapper
@ -50,7 +66,11 @@ describe('NotePreview', () => {
describe('UpdatedBy', () => {
test('it renders the username when updatedBy is provided', () => {
const wrapper = mountWithIntl(<NotePreview updatedBy="admin" />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview updatedBy="admin" />
</ThemeProvider>
);
expect(
wrapper
@ -61,7 +81,11 @@ describe('NotePreview', () => {
});
test('it renders placeholder text when updatedBy is undefined', () => {
const wrapper = mountWithIntl(<NotePreview />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview />
</ThemeProvider>
);
expect(
wrapper
@ -72,7 +96,11 @@ describe('NotePreview', () => {
});
test('it renders placeholder text when updatedBy is null', () => {
const wrapper = mountWithIntl(<NotePreview updatedBy={null} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview updatedBy={null} />
</ThemeProvider>
);
expect(
wrapper
@ -87,7 +115,11 @@ describe('NotePreview', () => {
const updated = 1553300753 * 1000;
test('it is always prefixed by "Posted:"', () => {
const wrapper = mountWithIntl(<NotePreview updated={updated} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview updated={updated} />
</ThemeProvider>
);
expect(
wrapper
@ -99,7 +131,11 @@ describe('NotePreview', () => {
});
test('it renders the relative date when updated is provided', () => {
const wrapper = mountWithIntl(<NotePreview updated={updated} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview updated={updated} />
</ThemeProvider>
);
expect(
wrapper
@ -110,7 +146,11 @@ describe('NotePreview', () => {
});
test('it does NOT render the relative date when updated is undefined', () => {
const wrapper = mountWithIntl(<NotePreview />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview />
</ThemeProvider>
);
expect(
wrapper
@ -121,7 +161,11 @@ describe('NotePreview', () => {
});
test('it does NOT render the relative date when updated is null', () => {
const wrapper = mountWithIntl(<NotePreview updated={null} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview updated={null} />
</ThemeProvider>
);
expect(
wrapper
@ -132,7 +176,11 @@ describe('NotePreview', () => {
});
test('it renders placeholder text when updated is undefined', () => {
const wrapper = mountWithIntl(<NotePreview />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview />
</ThemeProvider>
);
expect(
wrapper
@ -143,7 +191,11 @@ describe('NotePreview', () => {
});
test('it renders placeholder text when updated is null', () => {
const wrapper = mountWithIntl(<NotePreview updated={null} />);
const wrapper = mountWithIntl(
<ThemeProvider theme={theme}>
<NotePreview updated={null} />
</ThemeProvider>
);
expect(
wrapper

View file

@ -4,31 +4,26 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiAvatar, EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer, EuiToolTip } from '@elastic/eui';
import { EuiAvatar, EuiFlexGroup, EuiFlexItem, EuiText, EuiTitle, EuiToolTip } from '@elastic/eui';
import { FormattedRelative } from '@kbn/i18n/react';
import * as React from 'react';
import { pure } from 'recompose';
import styled from 'styled-components';
import { FormattedDate } from '../../formatted_date';
import { getEmptyValue, defaultToEmptyTag } from '../../empty_value';
import { FormattedDate } from '../../formatted_date';
import { Markdown } from '../../markdown';
import * as i18n from '../translations';
import { TimelineResultNote } from '../types';
export const Avatar = styled(EuiAvatar)`
margin-right: 12px;
user-select: none;
const NotePreviewGroup = styled.article`
& + & {
margin-top: ${props => props.theme.eui.euiSizeL};
}
`;
const UpdatedBy = styled.div`
font-weight: bold;
`;
const NotePreviewFlexGroup = styled(EuiFlexGroup)`
margin: 16px 0;
width: 100%;
const NotePreviewHeader = styled.header`
margin-bottom: ${props => props.theme.eui.euiSizeS};
`;
/**
@ -36,31 +31,35 @@ const NotePreviewFlexGroup = styled(EuiFlexGroup)`
*/
export const NotePreview = pure<Pick<TimelineResultNote, 'note' | 'updated' | 'updatedBy'>>(
({ note, updated, updatedBy }) => (
<NotePreviewFlexGroup direction="row" gutterSize="none">
<EuiFlexItem grow={false}>
<Avatar data-test-subj="avatar" size="m" name={updatedBy != null ? updatedBy : '?'} />
</EuiFlexItem>
<NotePreviewGroup>
<EuiFlexGroup gutterSize="m" responsive={false}>
<EuiFlexItem grow={false}>
<EuiAvatar data-test-subj="avatar" name={updatedBy != null ? updatedBy : '?'} size="l" />
</EuiFlexItem>
<EuiFlexItem grow={true}>
<UpdatedBy data-test-subj="updated-by">{defaultToEmptyTag(updatedBy)}</UpdatedBy>
<EuiFlexItem>
<NotePreviewHeader>
<EuiTitle data-test-subj="updated-by" size="xxs">
<h6>{defaultToEmptyTag(updatedBy)}</h6>
</EuiTitle>
<div data-test-subj="posted">
<EuiText color="subdued" grow={true} size="xs">
{i18n.POSTED}{' '}
{updated != null ? (
<EuiToolTip content={<FormattedDate fieldName="" value={updated} />}>
<FormattedRelative data-test-subj="updated" value={new Date(updated)} />
</EuiToolTip>
) : (
getEmptyValue()
)}
</EuiText>
</div>
<EuiText color="subdued" data-test-subj="posted" size="xs">
<p>
{i18n.POSTED}{' '}
{updated != null ? (
<EuiToolTip content={<FormattedDate fieldName="" value={updated} />}>
<FormattedRelative data-test-subj="updated" value={new Date(updated)} />
</EuiToolTip>
) : (
getEmptyValue()
)}
</p>
</EuiText>
</NotePreviewHeader>
<EuiSpacer data-test-subj="posted-spacer" size="s" />
<Markdown raw={note || ''} size="xs" />
</EuiFlexItem>
</NotePreviewFlexGroup>
<Markdown raw={note || ''} size="s" />
</EuiFlexItem>
</EuiFlexGroup>
</NotePreviewGroup>
)
);

View file

@ -4,9 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { cloneDeep } from 'lodash/fp';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import { DEFAULT_SEARCH_RESULTS_PER_PAGE } from '../../pages/timelines/timelines_page';
import { OpenTimelineResult } from './types';
@ -16,6 +18,7 @@ import { OpenTimeline } from './open_timeline';
import { DEFAULT_SORT_DIRECTION, DEFAULT_SORT_FIELD } from './constants';
describe('OpenTimeline', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
const title = 'All Timelines / Open Timelines';
let mockResults: OpenTimelineResult[];
@ -26,30 +29,32 @@ describe('OpenTimeline', () => {
test('it renders the title row', () => {
const wrapper = mountWithIntl(
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
expect(
@ -60,68 +65,34 @@ describe('OpenTimeline', () => {
).toBe(true);
});
test('it renders the title row spacer', () => {
const wrapper = mountWithIntl(
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
);
expect(
wrapper
.find('[data-test-subj="title-row-spacer"]')
.first()
.exists()
).toBe(true);
});
test('it renders the search row', () => {
const wrapper = mountWithIntl(
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
expect(
@ -132,68 +103,34 @@ describe('OpenTimeline', () => {
).toBe(true);
});
test('it renders the search row spacer', () => {
const wrapper = mountWithIntl(
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
);
expect(
wrapper
.find('[data-test-subj="search-row-spacer"]')
.first()
.exists()
).toBe(true);
});
test('it renders the timelines table', () => {
const wrapper = mountWithIntl(
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
expect(
@ -206,30 +143,32 @@ describe('OpenTimeline', () => {
test('it shows extended columns and actions when onDeleteSelected and deleteTimelines are specified', () => {
const wrapper = mountWithIntl(
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
const props = wrapper
@ -242,29 +181,31 @@ describe('OpenTimeline', () => {
test('it does NOT show extended columns and actions when is onDeleteSelected undefined and deleteTimelines is specified', () => {
const wrapper = mountWithIntl(
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimeline
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
const props = wrapper
@ -277,29 +218,31 @@ describe('OpenTimeline', () => {
test('it does NOT show extended columns and actions when is onDeleteSelected provided and deleteTimelines is undefined', () => {
const wrapper = mountWithIntl(
<OpenTimeline
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimeline
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
const props = wrapper
@ -312,28 +255,30 @@ describe('OpenTimeline', () => {
test('it does NOT show extended columns and actions when both onDeleteSelected and deleteTimelines are undefined', () => {
const wrapper = mountWithIntl(
<OpenTimeline
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimeline
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
const props = wrapper

View file

@ -4,10 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiPanel, EuiSpacer } from '@elastic/eui';
import { EuiPanel } from '@elastic/eui';
import * as React from 'react';
import { pure } from 'recompose';
import styled from 'styled-components';
import { OPEN_TIMELINE_CLASS_NAME } from './helpers';
import { OpenTimelineProps } from './types';
@ -15,10 +14,6 @@ import { SearchRow } from './search_row';
import { TimelinesTable } from './timelines_table';
import { TitleRow } from './title_row';
export const OpenTimelinePanel = styled(EuiPanel)`
width: 100%;
`;
export const OpenTimeline = pure<OpenTimelineProps>(
({
deleteTimelines,
@ -44,7 +39,7 @@ export const OpenTimeline = pure<OpenTimelineProps>(
title,
totalSearchResultsCount,
}) => (
<OpenTimelinePanel className={OPEN_TIMELINE_CLASS_NAME} paddingSize="l">
<EuiPanel className={OPEN_TIMELINE_CLASS_NAME}>
<TitleRow
data-test-subj="title-row"
onDeleteSelected={onDeleteSelected}
@ -53,8 +48,6 @@ export const OpenTimeline = pure<OpenTimelineProps>(
title={title}
/>
<EuiSpacer data-test-subj="title-row-spacer" size="l" />
<SearchRow
data-test-subj="search-row"
onlyFavorites={onlyFavorites}
@ -64,8 +57,6 @@ export const OpenTimeline = pure<OpenTimelineProps>(
totalSearchResultsCount={totalSearchResultsCount}
/>
<EuiSpacer data-test-subj="search-row-spacer" size="l" />
<TimelinesTable
data-test-subj="timelines-table"
deleteTimelines={deleteTimelines}
@ -84,6 +75,6 @@ export const OpenTimeline = pure<OpenTimelineProps>(
sortField={sortField}
totalSearchResultsCount={totalSearchResultsCount}
/>
</OpenTimelinePanel>
</EuiPanel>
)
);

View file

@ -4,10 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { get } from 'lodash/fp';
import { mount, ReactWrapper } from 'enzyme';
import * as React from 'react';
import { MockedProvider } from 'react-apollo/test-utils';
import { ThemeProvider } from 'styled-components';
import { wait } from '../../../lib/helpers';
import { TestProviderWithoutDragAndDrop } from '../../../mock/test_providers';
@ -29,9 +31,12 @@ React.Component<{}, {}, any> =>
.childAt(0)
.childAt(0)
.childAt(0)
.childAt(0)
.instance();
describe('OpenTimelineModalButton', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
test('it renders the expected button text', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
@ -56,11 +61,13 @@ describe('OpenTimelineModalButton', () => {
describe('statefulness', () => {
test('defaults showModal to false', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={jest.fn()} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={jest.fn()} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -72,11 +79,13 @@ describe('OpenTimelineModalButton', () => {
test('it sets showModal to true when the button is clicked', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={jest.fn()} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={jest.fn()} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -114,11 +123,13 @@ describe('OpenTimelineModalButton', () => {
test('it renders the modal when showModal is true', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={jest.fn()} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={jest.fn()} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -142,11 +153,13 @@ describe('OpenTimelineModalButton', () => {
describe('onToggle prop', () => {
test('it still correctly updates the showModal state if `onToggle` is not provided as a prop', async () => {
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={jest.fn()} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={jest.fn()} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();
@ -164,11 +177,13 @@ describe('OpenTimelineModalButton', () => {
test('it invokes the optional onToggle function provided as a prop when the open timeline button is clicked', async () => {
const onToggle = jest.fn();
const wrapper = mount(
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={onToggle} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
<ThemeProvider theme={theme}>
<TestProviderWithoutDragAndDrop>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<OpenTimelineModalButton onToggle={onToggle} />
</MockedProvider>
</TestProviderWithoutDragAndDrop>
</ThemeProvider>
);
await wait();

View file

@ -4,9 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { cloneDeep } from 'lodash/fp';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import { DEFAULT_SEARCH_RESULTS_PER_PAGE } from '../../../pages/timelines/timelines_page';
import { OpenTimelineResult } from '../types';
@ -16,6 +18,7 @@ import { OpenTimelineModal } from './open_timeline_modal';
import { DEFAULT_SORT_DIRECTION, DEFAULT_SORT_FIELD } from '../constants';
describe('OpenTimelineModal', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
const title = 'All Timelines / Open Timelines';
let mockResults: OpenTimelineResult[];
@ -26,30 +29,32 @@ describe('OpenTimelineModal', () => {
test('it renders the title row', () => {
const wrapper = mountWithIntl(
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
expect(
@ -60,68 +65,34 @@ describe('OpenTimelineModal', () => {
).toBe(true);
});
test('it renders the title row spacer', () => {
const wrapper = mountWithIntl(
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
);
expect(
wrapper
.find('[data-test-subj="title-row-spacer"]')
.first()
.exists()
).toBe(true);
});
test('it renders the search row', () => {
const wrapper = mountWithIntl(
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
expect(
@ -134,30 +105,32 @@ describe('OpenTimelineModal', () => {
test('it renders the timelines table', () => {
const wrapper = mountWithIntl(
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
expect(
@ -170,30 +143,32 @@ describe('OpenTimelineModal', () => {
test('it shows extended columns and actions when onDeleteSelected and deleteTimelines are specified', () => {
const wrapper = mountWithIntl(
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
const props = wrapper
@ -206,29 +181,31 @@ describe('OpenTimelineModal', () => {
test('it does NOT show extended columns and actions when is onDeleteSelected undefined and deleteTimelines is specified', () => {
const wrapper = mountWithIntl(
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimelineModal
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
const props = wrapper
@ -241,29 +218,31 @@ describe('OpenTimelineModal', () => {
test('it does NOT show extended columns and actions when is onDeleteSelected provided and deleteTimelines is undefined', () => {
const wrapper = mountWithIntl(
<OpenTimelineModal
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimelineModal
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
const props = wrapper
@ -276,28 +255,30 @@ describe('OpenTimelineModal', () => {
test('it does NOT show extended columns and actions when both onDeleteSelected and deleteTimelines are undefined', () => {
const wrapper = mountWithIntl(
<OpenTimelineModal
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
<ThemeProvider theme={theme}>
<OpenTimelineModal
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
isLoading={false}
itemIdToExpandedNotesRowMap={{}}
onAddTimelinesToFavorites={jest.fn()}
onlyFavorites={false}
onOpenTimeline={jest.fn()}
onQueryChange={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
query={''}
searchResults={mockResults}
selectedItems={[]}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
title={title}
totalSearchResultsCount={mockResults.length}
/>
</ThemeProvider>
);
const props = wrapper

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiModalBody, EuiModalHeader, EuiSpacer } from '@elastic/eui';
import { EuiModalBody, EuiModalHeader } from '@elastic/eui';
import * as React from 'react';
import { pure } from 'recompose';
import styled from 'styled-components';
@ -54,8 +54,6 @@ export const OpenTimelineModal = pure<OpenTimelineProps>(
title={title}
/>
<EuiSpacer data-test-subj="title-row-spacer" size="l" />
<SearchRow
data-test-subj="search-row"
onlyFavorites={onlyFavorites}

View file

@ -5,23 +5,29 @@
*/
import { EuiFilterButtonProps } from '@elastic/eui';
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import { SearchRow } from '.';
import * as i18n from '../translations';
describe('SearchRow', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
test('it renders a search input with the expected placeholder when the query is empty', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={0}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={0}
/>
</ThemeProvider>
);
expect(
@ -35,13 +41,15 @@ describe('SearchRow', () => {
describe('Only Favorites Button', () => {
test('it renders the expected button text', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={0}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={0}
/>
</ThemeProvider>
);
expect(
@ -56,13 +64,15 @@ describe('SearchRow', () => {
const onToggleOnlyFavorites = jest.fn();
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={onToggleOnlyFavorites}
query=""
totalSearchResultsCount={0}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={onToggleOnlyFavorites}
query=""
totalSearchResultsCount={0}
/>
</ThemeProvider>
);
wrapper
@ -75,13 +85,15 @@ describe('SearchRow', () => {
test('it sets the button to the toggled state when onlyFavorites is true', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={true}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={0}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={true}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={0}
/>
</ThemeProvider>
);
const props = wrapper
@ -94,13 +106,15 @@ describe('SearchRow', () => {
test('it sets the button to the NON-toggled state when onlyFavorites is false', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={0}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={0}
/>
</ThemeProvider>
);
const props = wrapper
@ -117,13 +131,15 @@ describe('SearchRow', () => {
test('it invokes onQueryChange when the user enters a query', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={onQueryChange}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={32}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={onQueryChange}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={32}
/>
</ThemeProvider>
);
wrapper
@ -137,13 +153,15 @@ describe('SearchRow', () => {
describe('Showing message', () => {
test('it renders the expected message when the query is an empty string', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={32}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={32}
/>
</ThemeProvider>
);
expect(
@ -151,18 +169,20 @@ describe('SearchRow', () => {
.find('[data-test-subj="query-message"]')
.first()
.text()
).toContain('Showing 32 Timelines');
).toContain('Showing: 32 timelines ');
});
test('it renders the expected message when the query just has whitespace', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=" "
totalSearchResultsCount={32}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=" "
totalSearchResultsCount={32}
/>
</ThemeProvider>
);
expect(
@ -170,18 +190,20 @@ describe('SearchRow', () => {
.find('[data-test-subj="query-message"]')
.first()
.text()
).toContain('Showing 32 Timelines');
).toContain('Showing: 32 timelines ');
});
test('it includes the word "with" when the query has non-whitespace characters', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query="How was your day?"
totalSearchResultsCount={32}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query="How was your day?"
totalSearchResultsCount={32}
/>
</ThemeProvider>
);
expect(
@ -189,20 +211,22 @@ describe('SearchRow', () => {
.find('[data-test-subj="query-message"]')
.first()
.text()
).toContain('Showing 32 Timelines with');
).toContain('Showing: 32 timelines with');
});
});
describe('selectable query text', () => {
test('it renders an empty string when the query is an empty string', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={32}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=""
totalSearchResultsCount={32}
/>
</ThemeProvider>
);
expect(
@ -215,13 +239,15 @@ describe('SearchRow', () => {
test('it renders the expected message when the query just has spaces', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=" "
totalSearchResultsCount={32}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=" "
totalSearchResultsCount={32}
/>
</ThemeProvider>
);
expect(
@ -234,13 +260,15 @@ describe('SearchRow', () => {
test('it echos the query when the query has non-whitespace characters', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query="Would you like to go to Denver?"
totalSearchResultsCount={32}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query="Would you like to go to Denver?"
totalSearchResultsCount={32}
/>
</ThemeProvider>
);
expect(
@ -253,13 +281,15 @@ describe('SearchRow', () => {
test('trims whitespace from the ends of the query', () => {
const wrapper = mountWithIntl(
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=" Is it starting to feel cramped in here? "
totalSearchResultsCount={32}
/>
<ThemeProvider theme={theme}>
<SearchRow
onlyFavorites={false}
onQueryChange={jest.fn()}
onToggleOnlyFavorites={jest.fn()}
query=" Is it starting to feel cramped in here? "
totalSearchResultsCount={32}
/>
</ThemeProvider>
);
expect(

View file

@ -11,7 +11,6 @@ import {
EuiFlexItem,
// @ts-ignore
EuiSearchBar,
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
@ -19,25 +18,17 @@ import * as React from 'react';
import { pure } from 'recompose';
import styled from 'styled-components';
import * as i18n from '../translations';
import { OpenTimelineProps } from '../types';
import * as i18n from '../translations';
const ShowingContainer = styled.div`
user-select: none;
const SearchRowContainer = styled.div`
&:not(:last-child) {
margin-bottom: ${props => props.theme.eui.euiSizeL};
}
`;
const SearchRowFlexGroup = styled(EuiFlexGroup)`
user-select: none;
`;
const FilterGroupFlexItem = styled(EuiFlexItem)`
margin-left: 24px;
`;
const SelectableQueryText = styled.span`
margin-left: 3px;
user-select: text;
margin-bottom: ${props => props.theme.eui.euiSizeXS};
`;
type Props = Pick<
@ -50,14 +41,9 @@ type Props = Pick<
*/
export const SearchRow = pure<Props>(
({ onlyFavorites, onQueryChange, onToggleOnlyFavorites, query, totalSearchResultsCount }) => (
<>
<SearchRowFlexGroup
alignItems="flexStart"
direction="row"
gutterSize="none"
justifyContent="spaceBetween"
>
<EuiFlexItem grow={true}>
<SearchRowContainer>
<SearchRowFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiSearchBar
data-test-subj="search-bar"
box={{
@ -68,7 +54,7 @@ export const SearchRow = pure<Props>(
/>
</EuiFlexItem>
<FilterGroupFlexItem grow={false}>
<EuiFlexItem grow={false}>
<EuiFilterGroup>
<EuiFilterButton
data-test-subj="only-favorites-toggle"
@ -78,27 +64,26 @@ export const SearchRow = pure<Props>(
{i18n.ONLY_FAVORITES}
</EuiFilterButton>
</EuiFilterGroup>
</FilterGroupFlexItem>
</EuiFlexItem>
</SearchRowFlexGroup>
<EuiSpacer size="s" />
<ShowingContainer data-test-subj="showing">
<EuiText color="subdued" size="xs">
<EuiText color="subdued" size="xs">
<p>
<FormattedMessage
data-test-subj="query-message"
id="xpack.siem.open.timeline.showingNTimelinesLabel"
defaultMessage="Showing {totalSearchResultsCount} {totalSearchResultsCount, plural, one {Timeline} other {Timelines}} {with}"
defaultMessage="Showing: {totalSearchResultsCount} {totalSearchResultsCount, plural, one {timeline} other {timelines}} {with}"
values={{
totalSearchResultsCount,
with: query.trim().length ? i18n.WITH : '',
with: (
<span data-test-subj="selectable-query-text">
{query.trim().length ? `${i18n.WITH} "${query.trim()}"` : ''}
</span>
),
}}
/>
<SelectableQueryText data-test-subj="selectable-query-text">
{query.trim()}
</SelectableQueryText>
</EuiText>
</ShowingContainer>
</>
</p>
</EuiText>
</SearchRowContainer>
)
);

View file

@ -4,20 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiButtonIcon, EuiToolTip, EuiIcon } from '@elastic/eui';
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import * as React from 'react';
import styled from 'styled-components';
import { ACTION_COLUMN_WIDTH, PositionedIcon } from './common_styles';
import { DeleteTimelines, OnOpenTimeline, OpenTimelineResult } from '../types';
import { ACTION_COLUMN_WIDTH } from './common_styles';
import { DeleteTimelineModalButton } from '../delete_timeline_modal';
import * as i18n from '../translations';
const HeaderIcon = styled(EuiIcon)`
position: relative;
left: 9px;
`;
import { DeleteTimelines, OnOpenTimeline, OpenTimelineResult } from '../types';
/**
* Returns the action columns (e.g. delete, open duplicate timeline)
@ -31,61 +24,48 @@ export const getActionsColumns = ({
onOpenTimeline: OnOpenTimeline;
showDeleteAction: boolean;
}) => {
const deleteTimelineColumn = {
align: 'right',
name: (
<EuiToolTip content={i18n.DELETE}>
<HeaderIcon data-test-subj="delete-header-icon" size="s" color="subdued" type="trash" />
</EuiToolTip>
),
const openAsDuplicateColumn = {
align: 'center',
field: 'savedObjectId',
render: (savedObjectId: string, { title }: OpenTimelineResult) => (
<PositionedIcon>
<DeleteTimelineModalButton
deleteTimelines={deleteTimelines}
savedObjectId={savedObjectId}
title={title}
name: '',
render: (savedObjectId: string, timelineResult: OpenTimelineResult) => (
<EuiToolTip content={i18n.OPEN_AS_DUPLICATE}>
<EuiButtonIcon
aria-label={i18n.OPEN_AS_DUPLICATE}
data-test-subj="open-duplicate"
isDisabled={savedObjectId == null}
iconSize="s"
iconType="copy"
onClick={() =>
onOpenTimeline({
duplicate: true,
timelineId: `${timelineResult.savedObjectId}`,
})
}
size="s"
/>
</PositionedIcon>
</EuiToolTip>
),
sortable: false,
width: ACTION_COLUMN_WIDTH,
};
const openAsDuplicateColumn = {
align: 'right',
name: (
<EuiToolTip content={i18n.OPEN_AS_DUPLICATE}>
<HeaderIcon data-test-subj="duplicate-header-icon" size="s" color="subdued" type="copy" />
</EuiToolTip>
),
const deleteTimelineColumn = {
align: 'center',
field: 'savedObjectId',
render: (savedObjectId: string, timelineResult: OpenTimelineResult) => (
<PositionedIcon>
<EuiToolTip content={i18n.OPEN_AS_DUPLICATE}>
<EuiButtonIcon
aria-label={i18n.OPEN_AS_DUPLICATE}
color="subdued"
data-test-subj="open-duplicate"
isDisabled={savedObjectId == null}
iconSize="s"
iconType="copy"
onClick={() =>
onOpenTimeline({
duplicate: true,
timelineId: `${timelineResult.savedObjectId}`,
})
}
size="s"
/>
</EuiToolTip>
</PositionedIcon>
name: '',
render: (savedObjectId: string, { title }: OpenTimelineResult) => (
<DeleteTimelineModalButton
deleteTimelines={deleteTimelines}
savedObjectId={savedObjectId}
title={title}
/>
),
sortable: false,
width: ACTION_COLUMN_WIDTH,
};
return showDeleteAction && deleteTimelines != null
? [deleteTimelineColumn, openAsDuplicateColumn]
? [openAsDuplicateColumn, deleteTimelineColumn]
: [openAsDuplicateColumn];
};

View file

@ -5,9 +5,11 @@
*/
import { EuiButtonIconProps } from '@elastic/eui';
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { cloneDeep, omit } from 'lodash/fp';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { DEFAULT_SEARCH_RESULTS_PER_PAGE } from '../../../pages/timelines/timelines_page';
import { getEmptyValue } from '../../empty_value';
@ -20,6 +22,7 @@ import * as i18n from '../translations';
import { DEFAULT_SORT_DIRECTION, DEFAULT_SORT_FIELD } from '../constants';
describe('#getCommonColumns', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
let mockResults: OpenTimelineResult[];
beforeEach(() => {
@ -226,23 +229,25 @@ describe('#getCommonColumns', () => {
};
const wrapper = mountWithIntl(
<TimelinesTable
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
loading={false}
itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap}
onOpenTimeline={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
searchResults={hasNotes}
showExtendedColumnsAndActions={true}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
totalSearchResultsCount={hasNotes.length}
/>
<ThemeProvider theme={theme}>
<TimelinesTable
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
loading={false}
itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap}
onOpenTimeline={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={jest.fn()}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
searchResults={hasNotes}
showExtendedColumnsAndActions={true}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
totalSearchResultsCount={hasNotes.length}
/>
</ThemeProvider>
);
const props = wrapper
@ -304,23 +309,25 @@ describe('#getCommonColumns', () => {
};
const wrapper = mountWithIntl(
<TimelinesTable
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
loading={false}
itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap}
onOpenTimeline={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={onToggleShowNotes}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
searchResults={hasNotes}
showExtendedColumnsAndActions={true}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
totalSearchResultsCount={hasNotes.length}
/>
<ThemeProvider theme={theme}>
<TimelinesTable
deleteTimelines={jest.fn()}
defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
loading={false}
itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap}
onOpenTimeline={jest.fn()}
onSelectionChange={jest.fn()}
onTableChange={jest.fn()}
onToggleShowNotes={onToggleShowNotes}
pageIndex={0}
pageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE}
searchResults={hasNotes}
showExtendedColumnsAndActions={true}
sortDirection={DEFAULT_SORT_DIRECTION}
sortField={DEFAULT_SORT_FIELD}
totalSearchResultsCount={hasNotes.length}
/>
</ThemeProvider>
);
wrapper

View file

@ -4,28 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { omit } from 'lodash/fp';
import { EuiButtonIcon, EuiLink } from '@elastic/eui';
import { omit } from 'lodash/fp';
import * as React from 'react';
import styled from 'styled-components';
import { FormattedDate } from '../../formatted_date';
import { getEmptyTagValue } from '../../empty_value';
import { ACTION_COLUMN_WIDTH } from './common_styles';
import { isUntitled } from '../helpers';
import { NotePreviews } from '../note_previews';
import { OnOpenTimeline, OnToggleShowNotes, OpenTimelineResult } from '../types';
import * as i18n from '../translations';
/** the width of the description column when showing extended columns */
export const EXTENDED_COLUMNS_DESCRIPTION_WIDTH = '30%';
export const DESCRIPTION_WIDTH = '45%';
const ExpandButtonContainer = styled.div`
position: relative;
top -4px;
`;
import { OnOpenTimeline, OnToggleShowNotes, OpenTimelineResult } from '../types';
import { getEmptyTagValue } from '../../empty_value';
import { FormattedDate } from '../../formatted_date';
/**
* Returns the column definitions (passed as the `columns` prop to
@ -47,25 +36,23 @@ export const getCommonColumns = ({
isExpander: true,
render: ({ notes, savedObjectId }: OpenTimelineResult) =>
notes != null && notes.length > 0 && savedObjectId != null ? (
<ExpandButtonContainer>
<EuiButtonIcon
data-test-subj="expand-notes"
onClick={() =>
itemIdToExpandedNotesRowMap[savedObjectId] != null
? onToggleShowNotes(omit(savedObjectId, itemIdToExpandedNotesRowMap))
: onToggleShowNotes({
...itemIdToExpandedNotesRowMap,
[savedObjectId]: (
<NotePreviews notes={notes} isModal={!showExtendedColumnsAndActions} />
),
})
}
aria-label={itemIdToExpandedNotesRowMap[savedObjectId] ? i18n.COLLAPSE : i18n.EXPAND}
iconType={itemIdToExpandedNotesRowMap[savedObjectId] ? 'arrowDown' : 'arrowRight'}
/>
</ExpandButtonContainer>
<EuiButtonIcon
data-test-subj="expand-notes"
onClick={() =>
itemIdToExpandedNotesRowMap[savedObjectId] != null
? onToggleShowNotes(omit(savedObjectId, itemIdToExpandedNotesRowMap))
: onToggleShowNotes({
...itemIdToExpandedNotesRowMap,
[savedObjectId]: (
<NotePreviews notes={notes} isModal={!showExtendedColumnsAndActions} />
),
})
}
aria-label={itemIdToExpandedNotesRowMap[savedObjectId] ? i18n.COLLAPSE : i18n.EXPAND}
iconType={itemIdToExpandedNotesRowMap[savedObjectId] ? 'arrowDown' : 'arrowRight'}
/>
) : null,
width: '32px',
width: ACTION_COLUMN_WIDTH,
},
{
dataType: 'string',
@ -101,7 +88,6 @@ export const getCommonColumns = ({
</span>
),
sortable: false,
width: showExtendedColumnsAndActions ? EXTENDED_COLUMNS_DESCRIPTION_WIDTH : DESCRIPTION_WIDTH,
},
{
dataType: 'date',

View file

@ -4,21 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiAvatar } from '@elastic/eui';
import styled from 'styled-components';
export const PositionedIcon = styled.div`
position: relative;
top: -2px;
`;
/**
* The width of an action column, which must be wide enough to render a
* two digit count without wrapping
*/
export const ACTION_COLUMN_WIDTH = '35px';
export const Avatar = styled(EuiAvatar)`
margin-right: 5px;
user-select: none;
`;
export const ACTION_COLUMN_WIDTH = '40px';

View file

@ -6,33 +6,22 @@
import { EuiIcon, EuiToolTip } from '@elastic/eui';
import * as React from 'react';
import styled from 'styled-components';
import { ACTION_COLUMN_WIDTH, PositionedIcon } from './common_styles';
import { FavoriteTimelineResult, OpenTimelineResult } from '../types';
import { ACTION_COLUMN_WIDTH } from './common_styles';
import { getNotesCount, getPinnedEventCount } from '../helpers';
import * as i18n from '../translations';
const PinnedIcon = styled(EuiIcon)`
position: relative;
left: -3px;
`;
const CommentIcon = styled(EuiIcon)`
position: relative;
left: -2px;
`;
import { FavoriteTimelineResult, OpenTimelineResult } from '../types';
/**
* Returns the columns that have icon headers
*/
export const getIconHeaderColumns = () => [
{
align: 'center',
field: 'pinnedEventIds',
name: (
<EuiToolTip content={i18n.PINNED_EVENTS}>
<PinnedIcon data-test-subj="pinned-event-header-icon" size="m" color="subdued" type="pin" />
<EuiIcon data-test-subj="pinned-event-header-icon" size="m" type="pin" />
</EuiToolTip>
),
render: (_: Record<string, boolean> | null | undefined, timelineResult: OpenTimelineResult) => (
@ -42,15 +31,11 @@ export const getIconHeaderColumns = () => [
width: ACTION_COLUMN_WIDTH,
},
{
align: 'center',
field: 'eventIdToNoteIds',
name: (
<EuiToolTip content={i18n.NOTES}>
<CommentIcon
data-test-subj="notes-count-header-icon"
size="m"
color="subdued"
type="editorComment"
/>
<EuiIcon data-test-subj="notes-count-header-icon" size="m" type="editorComment" />
</EuiToolTip>
),
render: (
@ -61,21 +46,18 @@ export const getIconHeaderColumns = () => [
width: ACTION_COLUMN_WIDTH,
},
{
align: 'center',
field: 'favorite',
name: (
<EuiToolTip content={i18n.FAVORITES}>
<EuiIcon data-test-subj="favorites-header-icon" size="m" color="subdued" type="starEmpty" />
<EuiIcon data-test-subj="favorites-header-icon" size="m" type="starEmpty" />
</EuiToolTip>
),
render: (favorite: FavoriteTimelineResult[] | null | undefined) => {
const isFavorite = favorite != null && favorite.length > 0;
const fill = isFavorite ? 'starFilled' : 'starEmpty';
return (
<PositionedIcon>
<EuiIcon data-test-subj={`favorite-${fill}-star`} type={fill} size="m" />
</PositionedIcon>
);
return <EuiIcon data-test-subj={`favorite-${fill}-star`} type={fill} size="m" />;
},
sortable: false,
width: ACTION_COLUMN_WIDTH,

View file

@ -24,21 +24,9 @@ import { getIconHeaderColumns } from './icon_header_columns';
import * as i18n from '../translations';
const TimelinesTableContainer = styled.div`
const BasicTable = styled(EuiBasicTable)`
.euiTableCellContent {
animation: none;
text-align: left;
}
.euiTableCellContent__text {
width: 100%;
}
tbody {
th,
td {
vertical-align: top;
}
}
`;
@ -146,30 +134,27 @@ export const TimelinesTable = pure<TimelinesTableProps>(
};
return (
<TimelinesTableContainer data-test-subj="timelines-table-container">
<EuiBasicTable
compressed={true}
columns={getTimelinesTableColumns({
deleteTimelines,
itemIdToExpandedNotesRowMap,
onOpenTimeline,
onToggleShowNotes,
showExtendedColumnsAndActions,
})}
data-test-subj="timelines-table"
isExpandable={true}
isSelectable={showExtendedColumnsAndActions}
itemId="savedObjectId"
itemIdToExpandedRowMap={itemIdToExpandedNotesRowMap}
items={searchResults}
loading={isLoading}
noItemsMessage={i18n.ZERO_TIMELINES_MATCH}
onChange={onTableChange}
pagination={pagination}
selection={showExtendedColumnsAndActions ? selection : undefined}
sorting={sorting}
/>
</TimelinesTableContainer>
<BasicTable
columns={getTimelinesTableColumns({
deleteTimelines,
itemIdToExpandedNotesRowMap,
onOpenTimeline,
onToggleShowNotes,
showExtendedColumnsAndActions,
})}
data-test-subj="timelines-table"
isExpandable={true}
isSelectable={showExtendedColumnsAndActions}
itemId="savedObjectId"
itemIdToExpandedRowMap={itemIdToExpandedNotesRowMap}
items={searchResults}
loading={isLoading}
noItemsMessage={i18n.ZERO_TIMELINES_MATCH}
onChange={onTableChange}
pagination={pagination}
selection={showExtendedColumnsAndActions ? selection : undefined}
sorting={sorting}
/>
);
}
);

View file

@ -4,28 +4,33 @@
* you may not use this file except in compliance with the Elastic License.
*/
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import { EuiButtonProps } from '@elastic/eui';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import { TitleRow } from '.';
describe('TitleRow', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
const title = 'All Timelines / Open Timelines';
test('it renders the title', () => {
const wrapper = mountWithIntl(
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
</ThemeProvider>
);
expect(
wrapper
.find('[data-test-subj="title"]')
.find('[data-test-subj="panel_headline_title"]')
.first()
.text()
).toEqual(title);
@ -34,12 +39,14 @@ describe('TitleRow', () => {
describe('Favorite Selected button', () => {
test('it renders the Favorite Selected button when onAddTimelinesToFavorites is provided', () => {
const wrapper = mountWithIntl(
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
</ThemeProvider>
);
expect(
@ -52,7 +59,9 @@ describe('TitleRow', () => {
test('it does NOT render the Favorite Selected button when onAddTimelinesToFavorites is NOT provided', () => {
const wrapper = mountWithIntl(
<TitleRow onDeleteSelected={jest.fn()} selectedTimelinesCount={0} title={title} />
<ThemeProvider theme={theme}>
<TitleRow onDeleteSelected={jest.fn()} selectedTimelinesCount={0} title={title} />
</ThemeProvider>
);
expect(
@ -65,12 +74,14 @@ describe('TitleRow', () => {
test('it disables the Favorite Selected button when the selectedTimelinesCount is 0', () => {
const wrapper = mountWithIntl(
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
</ThemeProvider>
);
const props = wrapper
@ -83,12 +94,14 @@ describe('TitleRow', () => {
test('it enables the Favorite Selected button when the selectedTimelinesCount is greater than 0', () => {
const wrapper = mountWithIntl(
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={3}
title={title}
/>
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={3}
title={title}
/>
</ThemeProvider>
);
const props = wrapper
@ -103,12 +116,14 @@ describe('TitleRow', () => {
const onAddTimelinesToFavorites = jest.fn();
const wrapper = mountWithIntl(
<TitleRow
onAddTimelinesToFavorites={onAddTimelinesToFavorites}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={3}
title={title}
/>
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={onAddTimelinesToFavorites}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={3}
title={title}
/>
</ThemeProvider>
);
wrapper
@ -123,12 +138,14 @@ describe('TitleRow', () => {
describe('Delete Selected button', () => {
test('it renders the Delete Selected button when onDeleteSelected is provided', () => {
const wrapper = mountWithIntl(
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
</ThemeProvider>
);
expect(
@ -141,7 +158,13 @@ describe('TitleRow', () => {
test('it does NOT render the Delete Selected button when onDeleteSelected is NOT provided', () => {
const wrapper = mountWithIntl(
<TitleRow onAddTimelinesToFavorites={jest.fn()} selectedTimelinesCount={0} title={title} />
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
</ThemeProvider>
);
expect(
@ -154,12 +177,14 @@ describe('TitleRow', () => {
test('it disables the Delete Selected button when the selectedTimelinesCount is 0', () => {
const wrapper = mountWithIntl(
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={0}
title={title}
/>
</ThemeProvider>
);
const props = wrapper
@ -172,12 +197,14 @@ describe('TitleRow', () => {
test('it enables the Delete Selected button when the selectedTimelinesCount is greater than 0', () => {
const wrapper = mountWithIntl(
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={1}
title={title}
/>
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={jest.fn()}
selectedTimelinesCount={1}
title={title}
/>
</ThemeProvider>
);
const props = wrapper
@ -192,12 +219,14 @@ describe('TitleRow', () => {
const onDeleteSelected = jest.fn();
const wrapper = mountWithIntl(
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={onDeleteSelected}
selectedTimelinesCount={1}
title={title}
/>
<ThemeProvider theme={theme}>
<TitleRow
onAddTimelinesToFavorites={jest.fn()}
onDeleteSelected={onDeleteSelected}
selectedTimelinesCount={1}
title={title}
/>
</ThemeProvider>
);
wrapper

View file

@ -4,22 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import * as React from 'react';
import { pure } from 'recompose';
import styled from 'styled-components';
import { OpenTimelineProps } from '../types';
import * as i18n from '../translations';
const ButtonFlexItem = styled(EuiFlexItem)`
margin-left: 5px;
`;
const TitleRowFlexGroup = styled(EuiFlexGroup)`
user-select: none;
`;
import { OpenTimelineProps } from '../types';
import { HeaderPanel } from '../../header_panel';
type Props = Pick<OpenTimelineProps, 'onAddTimelinesToFavorites' | 'onDeleteSelected' | 'title'> & {
/** The number of timelines currently selected */
@ -32,45 +23,38 @@ type Props = Pick<OpenTimelineProps, 'onAddTimelinesToFavorites' | 'onDeleteSele
*/
export const TitleRow = pure<Props>(
({ onAddTimelinesToFavorites, onDeleteSelected, selectedTimelinesCount, title }) => (
<TitleRowFlexGroup
alignItems="flexStart"
direction="row"
gutterSize="none"
justifyContent="spaceBetween"
>
<EuiFlexItem grow={true}>
<EuiTitle data-test-subj="title" size="m">
<h2>{title}</h2>
</EuiTitle>
</EuiFlexItem>
<HeaderPanel title={title}>
{(onAddTimelinesToFavorites || onDeleteSelected) && (
<EuiFlexGroup gutterSize="s" responsive={false}>
{onAddTimelinesToFavorites && (
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="favorite-selected"
iconSide="left"
iconType="starEmptySpace"
isDisabled={selectedTimelinesCount === 0}
onClick={onAddTimelinesToFavorites}
>
{i18n.FAVORITE_SELECTED}
</EuiButton>
</EuiFlexItem>
)}
{onAddTimelinesToFavorites && (
<ButtonFlexItem grow={false}>
<EuiButton
data-test-subj="favorite-selected"
iconSide="left"
iconType="starEmptySpace"
isDisabled={selectedTimelinesCount === 0}
onClick={onAddTimelinesToFavorites}
>
{i18n.FAVORITE_SELECTED}
</EuiButton>
</ButtonFlexItem>
{onDeleteSelected && (
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="delete-selected"
iconSide="left"
iconType="trash"
isDisabled={selectedTimelinesCount === 0}
onClick={onDeleteSelected}
>
{i18n.DELETE_SELECTED}
</EuiButton>
</EuiFlexItem>
)}
</EuiFlexGroup>
)}
{onDeleteSelected && (
<ButtonFlexItem grow={false}>
<EuiButton
data-test-subj="delete-selected"
iconSide="left"
iconType="trash"
isDisabled={selectedTimelinesCount === 0}
onClick={onDeleteSelected}
>
{i18n.DELETE_SELECTED}
</EuiButton>
</ButtonFlexItem>
)}
</TitleRowFlexGroup>
</HeaderPanel>
)
);

View file

@ -19,7 +19,7 @@ export const DELETE = i18n.translate('xpack.siem.open.timeline.deleteButton', {
});
export const DELETE_SELECTED = i18n.translate('xpack.siem.open.timeline.deleteSelectedButton', {
defaultMessage: 'Delete Selected',
defaultMessage: 'Delete selected',
});
export const DELETE_WARNING = i18n.translate('xpack.siem.open.timeline.deleteWarningLabel', {
@ -35,7 +35,7 @@ export const EXPAND = i18n.translate('xpack.siem.open.timeline.expandButton', {
});
export const FAVORITE_SELECTED = i18n.translate('xpack.siem.open.timeline.favoriteSelectedButton', {
defaultMessage: 'Favorite Selected',
defaultMessage: 'Favorite selected',
});
export const FAVORITES = i18n.translate('xpack.siem.open.timeline.favoritesTooltip', {
@ -43,7 +43,7 @@ export const FAVORITES = i18n.translate('xpack.siem.open.timeline.favoritesToolt
});
export const LAST_MODIFIED = i18n.translate('xpack.siem.open.timeline.lastModifiedTableHeader', {
defaultMessage: 'Last Modified',
defaultMessage: 'Last modified',
});
export const MISSING_SAVED_OBJECT_ID = i18n.translate(
@ -54,7 +54,7 @@ export const MISSING_SAVED_OBJECT_ID = i18n.translate(
);
export const MODIFIED_BY = i18n.translate('xpack.siem.open.timeline.modifiedByTableHeader', {
defaultMessage: 'Modified By',
defaultMessage: 'Modified by',
});
export const NOTES = i18n.translate('xpack.siem.open.timeline.notesTooltip', {
@ -62,7 +62,7 @@ export const NOTES = i18n.translate('xpack.siem.open.timeline.notesTooltip', {
});
export const ONLY_FAVORITES = i18n.translate('xpack.siem.open.timeline.onlyFavoritesButtonLabel', {
defaultMessage: 'Only Favorites',
defaultMessage: 'Only favorites',
});
export const OPEN_AS_DUPLICATE = i18n.translate('xpack.siem.open.timeline.openAsDuplicateTooltip', {
@ -70,7 +70,7 @@ export const OPEN_AS_DUPLICATE = i18n.translate('xpack.siem.open.timeline.openAs
});
export const OPEN_TIMELINE = i18n.translate('xpack.siem.open.timeline.openTimelineButton', {
defaultMessage: 'Open Timeline...',
defaultMessage: 'Open Timeline',
});
export const OPEN_TIMELINE_TITLE = i18n.translate('xpack.siem.open.timeline.openTimelineTitle', {
@ -78,7 +78,7 @@ export const OPEN_TIMELINE_TITLE = i18n.translate('xpack.siem.open.timeline.open
});
export const PINNED_EVENTS = i18n.translate('xpack.siem.open.timeline.pinnedEventsTooltip', {
defaultMessage: 'Pinned Events',
defaultMessage: 'Pinned events',
});
export const POSTED = i18n.translate('xpack.siem.open.timeline.postedLabel', {
@ -90,11 +90,11 @@ export const SEARCH_PLACEHOLDER = i18n.translate('xpack.siem.open.timeline.searc
});
export const TIMELINE_NAME = i18n.translate('xpack.siem.open.timeline.timelineNameTableHeader', {
defaultMessage: 'Timeline Name',
defaultMessage: 'Timeline name',
});
export const UNTITLED_TIMELINE = i18n.translate('xpack.siem.open.timeline.untitledTimelineLabel', {
defaultMessage: 'Untitled Timeline',
defaultMessage: 'Untitled timeline',
});
export const WITH = i18n.translate('xpack.siem.open.timeline.withLabel', {

View file

@ -16,46 +16,46 @@ export const AUTHENTICATIONS = i18n.translate(
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.authenticationsTable.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {User} other {Users}}`,
defaultMessage: `{totalCount, plural, =1 {user} other {users}}`,
});
export const LAST_SUCCESSFUL_SOURCE = i18n.translate(
'xpack.siem.authenticationsTable.lastSuccessfulSource',
{
defaultMessage: 'Last Successful Source',
defaultMessage: 'Last successful source',
}
);
export const LAST_SUCCESSFUL_DESTINATION = i18n.translate(
'xpack.siem.authenticationsTable.lastSuccessfulDestination',
{
defaultMessage: 'Last Successful Destination',
defaultMessage: 'Last successful destination',
}
);
export const LAST_SUCCESSFUL_TIME = i18n.translate(
'xpack.siem.authenticationsTable.lastSuccessfulTime',
{
defaultMessage: 'Last Success',
defaultMessage: 'Last success',
}
);
export const LAST_FAILED_SOURCE = i18n.translate(
'xpack.siem.authenticationsTable.lastFailedSource',
{
defaultMessage: 'Last Failed Source',
defaultMessage: 'Last failed source',
}
);
export const LAST_FAILED_DESTINATION = i18n.translate(
'xpack.siem.authenticationsTable.lastFailedDestination',
{
defaultMessage: 'Last Failed Destination',
defaultMessage: 'Last failed destination',
}
);
export const LAST_FAILED_TIME = i18n.translate('xpack.siem.authenticationsTable.lastFailedTime', {
defaultMessage: 'Last Failure',
defaultMessage: 'Last failure',
});
export const SUCCESSES = i18n.translate('xpack.siem.authenticationsTable.successes', {

View file

@ -13,7 +13,7 @@ export const EVENTS = i18n.translate('xpack.siem.eventsTable.eventsTitle', {
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.eventsTable.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {Event} other {Events}}`,
defaultMessage: `{totalCount, plural, =1 {event} other {events}}`,
});
export const TIMESTAMP = i18n.translate('xpack.siem.eventsTable.timestampTitle', {
@ -21,11 +21,11 @@ export const TIMESTAMP = i18n.translate('xpack.siem.eventsTable.timestampTitle',
});
export const HOST_NAME = i18n.translate('xpack.siem.eventsTable.hostsNameTitle', {
defaultMessage: 'Host Name',
defaultMessage: 'Host name',
});
export const EVENT_ACTION = i18n.translate('xpack.siem.eventsTable.eventTypeAction', {
defaultMessage: 'Event Action',
defaultMessage: 'Event action',
});
export const SOURCE = i18n.translate('xpack.siem.eventsTable.sourceTitle', {
@ -41,7 +41,7 @@ export const MESSAGE = i18n.translate('xpack.siem.eventsTable.messageTitle', {
});
export const EVENT_MODULE_DATASET = i18n.translate('xpack.siem.eventsTable.moduleDatasetTitle', {
defaultMessage: 'Module/Dataset',
defaultMessage: 'Module/dataset',
});
export const USER = i18n.translate('xpack.siem.eventsTable.userTitle', {

View file

@ -11,26 +11,26 @@ export const HOST_ID = i18n.translate('xpack.siem.host.details.overview.hostIdTi
});
export const FIRST_SEEN = i18n.translate('xpack.siem.host.details.firstSeenTitle', {
defaultMessage: 'First Seen',
defaultMessage: 'First seen',
});
export const LAST_SEEN = i18n.translate('xpack.siem.host.details.lastSeenTitle', {
defaultMessage: 'Last Seen',
defaultMessage: 'Last seen',
});
export const MAX_ANOMALY_SCORE_BY_JOB = i18n.translate(
'xpack.siem.host.details.overview.maxAnomalyScoreByJobTitle',
{
defaultMessage: 'Max Anomaly Score By Job',
defaultMessage: 'Max anomaly score by job',
}
);
export const IP_ADDRESSES = i18n.translate('xpack.siem.host.details.overview.ipAddressesTitle', {
defaultMessage: 'IP Addresses',
defaultMessage: 'IP addresses',
});
export const MAC_ADDRESSES = i18n.translate('xpack.siem.host.details.overview.macAddressesTitle', {
defaultMessage: 'MAC Addresses',
defaultMessage: 'MAC addresses',
});
export const PLATFORM = i18n.translate('xpack.siem.host.details.overview.platformTitle', {
@ -38,7 +38,7 @@ export const PLATFORM = i18n.translate('xpack.siem.host.details.overview.platfor
});
export const OS = i18n.translate('xpack.siem.host.details.overview.osTitle', {
defaultMessage: 'Operating System',
defaultMessage: 'Operating system',
});
export const FAMILY = i18n.translate('xpack.siem.host.details.overview.familyTitle', {
@ -56,7 +56,7 @@ export const ARCHITECTURE = i18n.translate('xpack.siem.host.details.architecture
export const CLOUD_PROVIDER = i18n.translate(
'xpack.siem.host.details.overview.cloudProviderTitle',
{
defaultMessage: 'Cloud Provider',
defaultMessage: 'Cloud provider',
}
);
@ -65,11 +65,11 @@ export const REGION = i18n.translate('xpack.siem.host.details.overview.regionTit
});
export const INSTANCE_ID = i18n.translate('xpack.siem.host.details.overview.instanceIdTitle', {
defaultMessage: 'Instance Id',
defaultMessage: 'Instance ID',
});
export const MACHINE_TYPE = i18n.translate('xpack.siem.host.details.overview.machineTypeTitle', {
defaultMessage: 'Machine Type',
defaultMessage: 'Machine type',
});
export const INSPECT_TITLE = i18n.translate('xpack.siem.host.details.overview.inspectTitle', {

View file

@ -76,8 +76,8 @@ export const getHostsColumns = (
name: (
<EuiToolTip content={i18n.FIRST_LAST_SEEN_TOOLTIP}>
<>
{i18n.LAST_SEEN}
<EuiIcon size="s" color="subdued" type="questionInCircle" className="eui-alignTop" />
{i18n.LAST_SEEN}{' '}
<EuiIcon size="s" color="subdued" type="iInCircle" className="eui-alignTop" />
</>
</EuiToolTip>
),

View file

@ -119,7 +119,7 @@ describe('Hosts Table', () => {
.find('.euiTable thead tr th button')
.at(1)
.text()
).toEqual('Last SeenClick to sort in ascending order');
).toEqual('Last seen Click to sort in ascending order');
expect(
wrapper
.find('.euiTable thead tr th button')

View file

@ -7,25 +7,21 @@
import { i18n } from '@kbn/i18n';
export const HOSTS = i18n.translate('xpack.siem.hostsTable.hostsTitle', {
defaultMessage: 'All Hosts',
defaultMessage: 'All hosts',
});
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.hostsTable.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {Host} other {Hosts}}`,
defaultMessage: `{totalCount, plural, =1 {host} other {hosts}}`,
});
export const NAME = i18n.translate('xpack.siem.hostsTable.nameTitle', {
defaultMessage: 'Name',
});
export const FIRST_SEEN = i18n.translate('xpack.siem.hostsTable.firstSeenTitle', {
defaultMessage: 'First Seen',
});
export const LAST_SEEN = i18n.translate('xpack.siem.hostsTable.lastSeenTitle', {
defaultMessage: 'Last Seen',
defaultMessage: 'Last seen',
});
export const FIRST_LAST_SEEN_TOOLTIP = i18n.translate(
@ -36,18 +32,13 @@ export const FIRST_LAST_SEEN_TOOLTIP = i18n.translate(
);
export const OS = i18n.translate('xpack.siem.hostsTable.osTitle', {
defaultMessage: 'OS',
defaultMessage: 'Operating system',
});
export const VERSION = i18n.translate('xpack.siem.hostsTable.versionTitle', {
defaultMessage: 'Version',
});
export const ROWS_2 = i18n.translate('xpack.siem.hostsTable.rows', {
values: { numRows: 2 },
defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}',
});
export const ROWS_5 = i18n.translate('xpack.siem.hostsTable.rows', {
values: { numRows: 5 },
defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}',

View file

@ -3,23 +3,25 @@
exports[`kpiHostsComponent render it should render KpiHostDetailsData 1`] = `
<EuiFlexGroup>
<Memo()
description="User Authentications"
description="User authentications"
enableAreaChart={true}
enableBarChart={true}
fields={
Array [
Object {
"color": "#00B3A4",
"description": "Success",
"description": "success",
"icon": "check",
"key": "authSuccess",
"name": "Succ.",
"value": null,
},
Object {
"color": "#920000",
"description": "Fail",
"description": "fail",
"icon": "cross",
"key": "authFailure",
"name": "Fail",
"value": null,
},
]
@ -36,7 +38,7 @@ exports[`kpiHostsComponent render it should render KpiHostDetailsData 1`] = `
Array [
Object {
"color": "#DB1374",
"description": "Source",
"description": "source",
"icon": "visMapCoordinate",
"key": "uniqueSourceIps",
"name": "Src.",
@ -44,9 +46,10 @@ exports[`kpiHostsComponent render it should render KpiHostDetailsData 1`] = `
},
Object {
"color": "#490092",
"description": "Destination",
"description": "destination",
"icon": "visMapCoordinate",
"key": "uniqueDestinationIps",
"name": "Dest.",
"value": null,
},
]
@ -78,23 +81,25 @@ exports[`kpiHostsComponent render it should render KpiHostsData 1`] = `
key="hosts"
/>
<Memo()
description="User Authentications"
description="User authentications"
enableAreaChart={true}
enableBarChart={true}
fields={
Array [
Object {
"color": "#00B3A4",
"description": "Success",
"description": "success",
"icon": "check",
"key": "authSuccess",
"name": "Succ.",
"value": null,
},
Object {
"color": "#920000",
"description": "Fail",
"description": "fail",
"icon": "cross",
"key": "authFailure",
"name": "Fail",
"value": null,
},
]
@ -111,7 +116,7 @@ exports[`kpiHostsComponent render it should render KpiHostsData 1`] = `
Array [
Object {
"color": "#DB1374",
"description": "Source",
"description": "source",
"icon": "visMapCoordinate",
"key": "uniqueSourceIps",
"name": "Src.",
@ -119,9 +124,10 @@ exports[`kpiHostsComponent render it should render KpiHostsData 1`] = `
},
Object {
"color": "#490092",
"description": "Destination",
"description": "destination",
"icon": "visMapCoordinate",
"key": "uniqueDestinationIps",
"name": "Dest.",
"value": null,
},
]

View file

@ -19,14 +19,16 @@ export const kpiHostDetailsMapping: Readonly<StatItems[]> = [
fields: [
{
key: 'authSuccess',
description: i18n.AUTHENTICATION_SUCCESS,
name: i18n.SUCCESS_CHART_LABEL,
description: i18n.SUCCESS_UNIT_LABEL,
value: null,
color: euiColorVis0,
icon: 'check',
},
{
key: 'authFailure',
description: i18n.AUTHENTICATION_FAILURE,
name: i18n.FAIL_CHART_LABEL,
description: i18n.FAIL_UNIT_LABEL,
value: null,
color: euiColorVis9,
icon: 'cross',
@ -35,7 +37,7 @@ export const kpiHostDetailsMapping: Readonly<StatItems[]> = [
enableAreaChart: true,
enableBarChart: true,
grow: 1,
description: i18n.AUTHENTICATION,
description: i18n.USER_AUTHENTICATIONS,
},
{
key: 'uniqueIps',
@ -43,15 +45,16 @@ export const kpiHostDetailsMapping: Readonly<StatItems[]> = [
fields: [
{
key: 'uniqueSourceIps',
name: i18n.UNIQUE_SOURCE_IPS_ABBREVIATION,
description: i18n.UNIQUE_SOURCE_IPS,
name: i18n.SOURCE_CHART_LABEL,
description: i18n.SOURCE_UNIT_LABEL,
value: null,
color: euiColorVis2,
icon: 'visMapCoordinate',
},
{
key: 'uniqueDestinationIps',
description: i18n.UNIQUE_DESTINATION_IPS,
name: i18n.DESTINATION_CHART_LABEL,
description: i18n.DESTINATION_UNIT_LABEL,
value: null,
color: euiColorVis3,
icon: 'visMapCoordinate',

View file

@ -35,14 +35,16 @@ export const kpiHostsMapping: Readonly<StatItems[]> = [
fields: [
{
key: 'authSuccess',
description: i18n.AUTHENTICATION_SUCCESS,
name: i18n.SUCCESS_CHART_LABEL,
description: i18n.SUCCESS_UNIT_LABEL,
value: null,
color: euiColorVis0,
icon: 'check',
},
{
key: 'authFailure',
description: i18n.AUTHENTICATION_FAILURE,
name: i18n.FAIL_CHART_LABEL,
description: i18n.FAIL_UNIT_LABEL,
value: null,
color: euiColorVis9,
icon: 'cross',
@ -51,7 +53,7 @@ export const kpiHostsMapping: Readonly<StatItems[]> = [
enableAreaChart: true,
enableBarChart: true,
grow: 4,
description: i18n.AUTHENTICATION,
description: i18n.USER_AUTHENTICATIONS,
},
{
key: 'uniqueIps',
@ -59,15 +61,16 @@ export const kpiHostsMapping: Readonly<StatItems[]> = [
fields: [
{
key: 'uniqueSourceIps',
name: i18n.UNIQUE_SOURCE_IPS_ABBREVIATION,
description: i18n.UNIQUE_SOURCE_IPS,
name: i18n.SOURCE_CHART_LABEL,
description: i18n.SOURCE_UNIT_LABEL,
value: null,
color: euiColorVis2,
icon: 'visMapCoordinate',
},
{
key: 'uniqueDestinationIps',
description: i18n.UNIQUE_DESTINATION_IPS,
name: i18n.DESTINATION_CHART_LABEL,
description: i18n.DESTINATION_UNIT_LABEL,
value: null,
color: euiColorVis3,
icon: 'visMapCoordinate',

View file

@ -5,54 +5,67 @@
*/
import { i18n } from '@kbn/i18n';
export const HOSTS = i18n.translate('xpack.siem.kpiHosts.source.hostsTitle', {
export const HOSTS = i18n.translate('xpack.siem.kpiHosts.hosts.title', {
defaultMessage: 'Hosts',
});
export const AGENTS = i18n.translate('xpack.siem.kpiHosts.source.agentsTitle', {
defaultMessage: 'Agents',
});
export const AUTHENTICATION_SUCCESS = i18n.translate(
'xpack.siem.kpiHosts.source.authenticationSuccessTitle',
export const USER_AUTHENTICATIONS = i18n.translate(
'xpack.siem.kpiHosts.userAuthentications.title',
{
defaultMessage: 'Success',
defaultMessage: 'User authentications',
}
);
export const AUTHENTICATION_FAILURE = i18n.translate(
'xpack.siem.kpiHosts.source.authenticationFailureTitle',
export const SUCCESS_UNIT_LABEL = i18n.translate(
'xpack.siem.kpiHosts.userAuthentications.successUnitLabel',
{
defaultMessage: 'success',
}
);
export const FAIL_UNIT_LABEL = i18n.translate(
'xpack.siem.kpiHosts.userAuthentications.failUnitLabel',
{
defaultMessage: 'fail',
}
);
export const SUCCESS_CHART_LABEL = i18n.translate(
'xpack.siem.kpiHosts.userAuthentications.successChartLabel',
{
defaultMessage: 'Succ.',
}
);
export const FAIL_CHART_LABEL = i18n.translate(
'xpack.siem.kpiHosts.userAuthentications.failChartLabel',
{
defaultMessage: 'Fail',
}
);
export const AUTHENTICATION = i18n.translate('xpack.siem.kpiHosts.source.authenticationTitle', {
defaultMessage: 'User Authentications',
});
export const ACTIVE_USERS = i18n.translate('xpack.siem.kpiHosts.source.activeUsersTitle', {
defaultMessage: 'Active Users',
});
export const UNIQUE_IPS = i18n.translate('xpack.siem.kpiHosts.source.uniqueIpsTitle', {
export const UNIQUE_IPS = i18n.translate('xpack.siem.kpiHosts.uniqueIps.title', {
defaultMessage: 'Unique IPs',
});
export const UNIQUE_SOURCE_IPS = i18n.translate('xpack.siem.kpiHosts.source.uniqueSourceIpsTitle', {
defaultMessage: 'Source',
export const SOURCE_UNIT_LABEL = i18n.translate('xpack.siem.kpiHosts.uniqueIps.sourceUnitLabel', {
defaultMessage: 'source',
});
export const UNIQUE_SOURCE_IPS_ABBREVIATION = i18n.translate(
'xpack.siem.kpiHosts.source.uniqueSourceIpsAbbreviationTitle',
export const DESTINATION_UNIT_LABEL = i18n.translate(
'xpack.siem.kpiHosts.uniqueIps.destinationUnitLabel',
{
defaultMessage: 'Src.',
defaultMessage: 'destination',
}
);
export const UNIQUE_DESTINATION_IPS = i18n.translate(
'xpack.siem.kpiHosts.source.uniqueDestinationIpsTitle',
export const SOURCE_CHART_LABEL = i18n.translate('xpack.siem.kpiHosts.uniqueIps.sourceChartLabel', {
defaultMessage: 'Src.',
});
export const DESTINATION_CHART_LABEL = i18n.translate(
'xpack.siem.kpiHosts.uniqueIps.destinationChartLabel',
{
defaultMessage: 'Destination',
defaultMessage: 'Dest.',
}
);

View file

@ -9,14 +9,14 @@ import { i18n } from '@kbn/i18n';
export const UNCOMMON_PROCESSES = i18n.translate(
'xpack.siem.authenticationsTable.uncommonProcessTable',
{
defaultMessage: 'Uncommon Processes',
defaultMessage: 'Uncommon processes',
}
);
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.uncommonProcessTable.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {Process} other {Processes}}`,
defaultMessage: `{totalCount, plural, =1 {process} other {processes}}`,
});
export const HOSTS = i18n.translate('xpack.siem.uncommonProcessTable.hostsTitle', {
@ -26,23 +26,23 @@ export const HOSTS = i18n.translate('xpack.siem.uncommonProcessTable.hostsTitle'
export const NUMBER_OF_HOSTS = i18n.translate(
'xpack.siem.uncommonProcessTable.numberOfHostsTitle',
{
defaultMessage: 'Number of Hosts',
defaultMessage: 'Number of hosts',
}
);
export const NUMBER_OF_INSTANCES = i18n.translate(
'xpack.siem.uncommonProcessTable.numberOfInstances',
{
defaultMessage: 'Number of Instances',
defaultMessage: 'Number of instances',
}
);
export const LAST_COMMAND = i18n.translate('xpack.siem.uncommonProcessTable.lastCommandTitle', {
defaultMessage: 'Last Command',
defaultMessage: 'Last command',
});
export const LAST_USER = i18n.translate('xpack.siem.uncommonProcessTable.lastUserTitle', {
defaultMessage: 'Last User',
defaultMessage: 'Last user',
});
export const NAME = i18n.translate('xpack.siem.uncommonProcessTable.nameTitle', {

View file

@ -163,8 +163,8 @@ export const getDomainsColumns = (
name: (
<EuiToolTip content={i18n.FIRST_LAST_SEEN_TOOLTIP}>
<>
{i18n.LAST_SEEN}
<EuiIcon size="s" color="subdued" type="questionInCircle" className="eui-alignTop" />
{i18n.LAST_SEEN}{' '}
<EuiIcon size="s" color="subdued" type="iInCircle" className="eui-alignTop" />
</>
</EuiToolTip>
),

View file

@ -108,7 +108,7 @@ describe('Domains Table Component', () => {
.find('.euiTable thead tr th button')
.first()
.text()
).toEqual('Domain NameClick to sort in ascending order');
).toEqual('Domain nameClick to sort in ascending order');
expect(
wrapper
.find('.euiTable thead tr th button')

View file

@ -13,14 +13,14 @@ export const DOMAINS = i18n.translate('xpack.siem.network.ipDetails.domainsTable
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.network.ipDetails.domainsTable.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {Domain} other {Domains}}`,
defaultMessage: `{totalCount, plural, =1 {domain} other {domains}}`,
});
// Columns
export const DOMAIN_NAME = i18n.translate(
'xpack.siem.network.ipDetails.domainsTable.columns.domainNameTitle',
{
defaultMessage: 'Domain Name',
defaultMessage: 'Domain name',
}
);
@ -48,42 +48,35 @@ export const PACKETS = i18n.translate(
export const UNIQUE_DESTINATIONS = i18n.translate(
'xpack.siem.network.ipDetails.domainsTable.columns.uniqueDestinationsTitle',
{
defaultMessage: 'Unique Destinations',
defaultMessage: 'Unique destinations',
}
);
export const UNIQUE_SOURCES = i18n.translate(
'xpack.siem.network.ipDetails.domainsTable.columns.uniqueSourcesTitle',
{
defaultMessage: 'Unique Sources',
defaultMessage: 'Unique sources',
}
);
export const UNIQUE_CLIENTS = i18n.translate(
'xpack.siem.network.ipDetails.domainsTable.columns.uniqueClientsTitle',
{
defaultMessage: 'Unique Servers',
defaultMessage: 'Unique servers',
}
);
export const UNIQUE_SERVERS = i18n.translate(
'xpack.siem.network.ipDetails.domainsTable.columns.uniqueServersTitle',
{
defaultMessage: 'Unique Clients',
}
);
export const FIRST_SEEN = i18n.translate(
'xpack.siem.network.ipDetails.domainsTable.columns.firstSeenTitle',
{
defaultMessage: 'First Seen',
defaultMessage: 'Unique clients',
}
);
export const LAST_SEEN = i18n.translate(
'xpack.siem.network.ipDetails.domainsTable.columns.lastSeenTitle',
{
defaultMessage: 'Last Seen',
defaultMessage: 'Last seen',
}
);
@ -94,21 +87,6 @@ export const FIRST_LAST_SEEN_TOOLTIP = i18n.translate(
}
);
// Direction Select
export const UNIDIRECTIONAL = i18n.translate(
'xpack.siem.network.ipDetails.domainsTable.unidirectionalDropDownOptionLabel',
{
defaultMessage: 'Unidirectional',
}
);
export const BIDIRECTIONAL = i18n.translate(
'xpack.siem.network.ipDetails.domainsTable.bidirectionalDropDownOptionLabel',
{
defaultMessage: 'Bidirectional',
}
);
// Row Select
export const ROWS_5 = i18n.translate('xpack.siem.network.ipDetails.domainsTable.rows', {
values: { numRows: 5 },
@ -119,7 +97,3 @@ export const ROWS_10 = i18n.translate('xpack.siem.network.ipDetails.domainsTable
values: { numRows: 10 },
defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}',
});
export const MORE = i18n.translate('xpack.siem.network.ipDetails.domainsTable.moreDescription', {
defaultMessage: 'More ...',
});

View file

@ -6,10 +6,6 @@
import { i18n } from '@kbn/i18n';
export const LAST_BEAT = i18n.translate('xpack.siem.network.ipDetails.ipOverview.lastBeatTitle', {
defaultMessage: 'Last Beat',
});
export const LOCATION = i18n.translate('xpack.siem.network.ipDetails.ipOverview.locationTitle', {
defaultMessage: 'Location',
});
@ -17,23 +13,23 @@ export const LOCATION = i18n.translate('xpack.siem.network.ipDetails.ipOverview.
export const AUTONOMOUS_SYSTEM = i18n.translate(
'xpack.siem.network.ipDetails.ipOverview.autonomousSystemTitle',
{
defaultMessage: 'Autonomous System',
defaultMessage: 'Autonomous system',
}
);
export const MAX_ANOMALY_SCORE_BY_JOB = i18n.translate(
'xpack.siem.network.ipDetails.ipOverview.maxAnomalyScoreByJobTitle',
{
defaultMessage: 'Max Anomaly Score By Job',
defaultMessage: 'Max anomaly score by job',
}
);
export const FIRST_SEEN = i18n.translate('xpack.siem.network.ipDetails.ipOverview.firstSeenTitle', {
defaultMessage: 'First Seen',
defaultMessage: 'First seen',
});
export const LAST_SEEN = i18n.translate('xpack.siem.network.ipDetails.ipOverview.lastSeenTitle', {
defaultMessage: 'Last Seen',
defaultMessage: 'Last seen',
});
export const HOST_ID = i18n.translate('xpack.siem.network.ipDetails.ipOverview.hostIdTitle', {
@ -41,7 +37,7 @@ export const HOST_ID = i18n.translate('xpack.siem.network.ipDetails.ipOverview.h
});
export const HOST_NAME = i18n.translate('xpack.siem.network.ipDetails.ipOverview.hostNameTitle', {
defaultMessage: 'Host Name',
defaultMessage: 'Host name',
});
export const WHOIS = i18n.translate('xpack.siem.network.ipDetails.ipOverview.whoIsTitle', {
@ -51,12 +47,21 @@ export const WHOIS = i18n.translate('xpack.siem.network.ipDetails.ipOverview.who
export const VIEW_WHOIS = i18n.translate('xpack.siem.network.ipDetails.ipOverview.viewWhoisTitle', {
defaultMessage: 'iana.org',
});
export const REPUTATION = i18n.translate(
'xpack.siem.network.ipDetails.ipOverview.ipReputationTitle',
{
defaultMessage: 'Reputation',
}
);
export const VIEW_VIRUS_TOTAL = i18n.translate(
'xpack.siem.network.ipDetails.ipOverview.viewVirusTotalTitle.',
{
defaultMessage: 'virustotal.com',
}
);
export const VIEW_TALOS_INTELLIGENCE = i18n.translate(
'xpack.siem.network.ipDetails.ipOverview.viewTalosIntelligenceTitle',
{
@ -64,13 +69,6 @@ export const VIEW_TALOS_INTELLIGENCE = i18n.translate(
}
);
export const REPUTATION = i18n.translate(
'xpack.siem.network.ipDetails.ipOverview.ipReputationTitle',
{
defaultMessage: 'Reputation',
}
);
export const AS_SOURCE = i18n.translate(
'xpack.siem.network.ipDetails.ipOverview.asSourceDropDownOptionLabel',
{

View file

@ -44,16 +44,16 @@ export const fieldTitleChartMapping: Readonly<StatItems[]> = [
{
key: 'uniqueSourcePrivateIps',
value: null,
name: i18n.SOURCE_NAME,
description: i18n.SOURCE,
name: i18n.SOURCE_CHART_LABEL,
description: i18n.SOURCE_UNIT_LABEL,
color: euiColorVis2,
icon: 'visMapCoordinate',
},
{
key: 'uniqueDestinationPrivateIps',
value: null,
name: i18n.DESTINATION_NAME,
description: i18n.DESTINATION,
name: i18n.DESTINATION_CHART_LABEL,
description: i18n.DESTINATION_UNIT_LABEL,
color: euiColorVis3,
icon: 'visMapCoordinate',
},

View file

@ -46,7 +46,7 @@ const mockMappingItems: StatItems = {
key: 'uniqueSourcePrivateIps',
value: null,
name: 'Src.',
description: 'Source',
description: 'source',
color: '#DB1374',
icon: 'visMapCoordinate',
},
@ -54,12 +54,12 @@ const mockMappingItems: StatItems = {
key: 'uniqueDestinationPrivateIps',
value: null,
name: 'Dest.',
description: 'Destination',
description: 'destination',
color: '#490092',
icon: 'visMapCoordinate',
},
],
description: 'Unique Private IPs',
description: 'Unique private IPs',
enableAreaChart: true,
enableBarChart: true,
grow: 2,
@ -79,7 +79,7 @@ export const mockDisableChartsInitialData = {
key: 'uniqueSourcePrivateIps',
value: undefined,
name: 'Src.',
description: 'Source',
description: 'source',
color: '#DB1374',
icon: 'visMapCoordinate',
},
@ -87,12 +87,12 @@ export const mockDisableChartsInitialData = {
key: 'uniqueDestinationPrivateIps',
value: undefined,
name: 'Dest.',
description: 'Destination',
description: 'destination',
color: '#490092',
icon: 'visMapCoordinate',
},
],
description: 'Unique Private IPs',
description: 'Unique private IPs',
enableAreaChart: false,
enableBarChart: false,
grow: 2,
@ -106,7 +106,7 @@ export const mockEnableChartsInitialData = {
key: 'uniqueSourcePrivateIps',
value: undefined,
name: 'Src.',
description: 'Source',
description: 'source',
color: '#DB1374',
icon: 'visMapCoordinate',
},
@ -114,12 +114,12 @@ export const mockEnableChartsInitialData = {
key: 'uniqueDestinationPrivateIps',
value: undefined,
name: 'Dest.',
description: 'Destination',
description: 'destination',
color: '#490092',
icon: 'visMapCoordinate',
},
],
description: 'Unique Private IPs',
description: 'Unique private IPs',
enableAreaChart: true,
enableBarChart: true,
grow: 2,
@ -162,7 +162,7 @@ export const mockEnableChartsData = {
},
],
name: 'Src.',
description: 'Source',
description: 'source',
color: '#DB1374',
icon: 'visMapCoordinate',
},
@ -173,7 +173,7 @@ export const mockEnableChartsData = {
{ x: new Date('2019-02-09T19:00:00.000Z').valueOf(), y: 0 },
],
name: 'Dest.',
description: 'Destination',
description: 'destination',
color: '#490092',
icon: 'visMapCoordinate',
},
@ -190,7 +190,7 @@ export const mockEnableChartsData = {
value: [{ x: 'Dest.', y: 18, g: 'uniqueDestinationPrivateIps' }],
},
],
description: 'Unique Private IPs',
description: 'Unique private IPs',
enableAreaChart: true,
enableBarChart: true,
fields: [
@ -198,7 +198,7 @@ export const mockEnableChartsData = {
key: 'uniqueSourcePrivateIps',
value: 383,
name: 'Src.',
description: 'Source',
description: 'source',
color: '#DB1374',
icon: 'visMapCoordinate',
},
@ -206,7 +206,7 @@ export const mockEnableChartsData = {
key: 'uniqueDestinationPrivateIps',
value: 18,
name: 'Dest.',
description: 'Destination',
description: 'destination',
color: '#490092',
icon: 'visMapCoordinate',
},

View file

@ -6,52 +6,50 @@
import { i18n } from '@kbn/i18n';
export const NETWORK_EVENTS = i18n.translate('xpack.siem.kpiNetwork.source.networkEventsTitle', {
defaultMessage: 'Network Events',
export const NETWORK_EVENTS = i18n.translate('xpack.siem.kpiNetwork.networkEvents.title', {
defaultMessage: 'Network events',
});
export const UNIQUE_FLOW_IDS = i18n.translate('xpack.siem.kpiNetwork.source.uniqueFlowIdsTitle', {
defaultMessage: 'Unique Flow IDs',
export const UNIQUE_FLOW_IDS = i18n.translate('xpack.siem.kpiNetwork.uniqueFlowIds.title', {
defaultMessage: 'Unique flow IDs',
});
export const ACTIVE_AGENTS = i18n.translate('xpack.siem.kpiNetwork.source.activeAgentsTitle', {
defaultMessage: 'Active Agents',
export const DNS_QUERIES = i18n.translate('xpack.siem.kpiNetwork.dnsQueries.title', {
defaultMessage: 'DNS queries',
});
export const UNIQUE_PRIVATE_IPS = i18n.translate(
'xpack.siem.kpiNetwork.source.uniquePrivateIpsTitle',
export const TLS_HANDSHAKES = i18n.translate('xpack.siem.kpiNetwork.tlsHandshakes.title', {
defaultMessage: 'TLS handshakes',
});
export const UNIQUE_PRIVATE_IPS = i18n.translate('xpack.siem.kpiNetwork.uniquePrivateIps.title', {
defaultMessage: 'Unique private IPs',
});
export const SOURCE_UNIT_LABEL = i18n.translate(
'xpack.siem.kpiNetwork.uniquePrivateIps.sourceUnitLabel',
{
defaultMessage: 'Unique Private IPs',
defaultMessage: 'source',
}
);
export const SOURCE = i18n.translate('xpack.siem.kpiNetwork.source.sourceTitle', {
defaultMessage: 'Source',
});
export const DESTINATION_UNIT_LABEL = i18n.translate(
'xpack.siem.kpiNetwork.uniquePrivateIps.destinationUnitLabel',
{
defaultMessage: 'destination',
}
);
export const SOURCE_NAME = i18n.translate('xpack.siem.kpiNetwork.source.name.sourceTitle', {
defaultMessage: 'Src.',
});
export const SOURCE_CHART_LABEL = i18n.translate(
'xpack.siem.kpiNetwork.uniquePrivateIps.sourceChartLabel',
{
defaultMessage: 'Src.',
}
);
export const DESTINATION = i18n.translate('xpack.siem.kpiNetwork.source.destinationTitle', {
defaultMessage: 'Destination',
});
export const DESTINATION_NAME = i18n.translate(
'xpack.siem.kpiNetwork.source.name.destinationTitle',
export const DESTINATION_CHART_LABEL = i18n.translate(
'xpack.siem.kpiNetwork.uniquePrivateIps.destinationChartLabel',
{
defaultMessage: 'Dest.',
}
);
export const LOADING = i18n.translate('xpack.siem.kpiNetwork.source.loadingDescription', {
defaultMessage: 'Loading',
});
export const DNS_QUERIES = i18n.translate('xpack.siem.kpiNetwork.source.dnsQueriesTitle', {
defaultMessage: 'DNS Queries',
});
export const TLS_HANDSHAKES = i18n.translate('xpack.siem.kpiNetwork.source.tlsHandshakesTitle', {
defaultMessage: 'TLS Handshakes',
});

View file

@ -7,50 +7,50 @@
import { i18n } from '@kbn/i18n';
export const TOP_DNS_DOMAINS = i18n.translate('xpack.siem.networkDnsTable.title', {
defaultMessage: 'Top DNS Domains',
defaultMessage: 'Top DNS domains',
});
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.networkDnsTable.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {Domain} other {Domains}}`,
defaultMessage: `{totalCount, plural, =1 {domain} other {domains}}`,
});
export const TOOLTIP = i18n.translate('xpack.siem.networkDnsTable.helperTooltip', {
defaultMessage:
'This shows DNS protocol traffic only, and can be useful for hunting domains used in DNS data exfiltration.',
'This shows DNS protocol traffic only and can be useful for hunting domains used in DNS data exfiltration.',
});
export const REGISTERED_DOMAIN = i18n.translate(
'xpack.siem.networkDnsTable.column.registeredDomain',
{
defaultMessage: 'Registered Domain',
defaultMessage: 'Registered domain',
}
);
export const TOTAL_QUERIES = i18n.translate('xpack.siem.networkDnsTable.column.TotalQueriesTitle', {
defaultMessage: 'Total Queries',
defaultMessage: 'Total queries',
});
export const UNIQUE_DOMAINS = i18n.translate(
'xpack.siem.networkDnsTable.column.uniqueDomainsTitle',
{
defaultMessage: 'Unique Domains',
defaultMessage: 'Unique domains',
}
);
export const DNS_BYTES_IN = i18n.translate('xpack.siem.networkDnsTable.column.bytesInTitle', {
defaultMessage: 'DNS Bytes In',
defaultMessage: 'DNS bytes in',
});
export const DNS_BYTES_OUT = i18n.translate('xpack.siem.networkDnsTable.column.bytesOutTitle', {
defaultMessage: 'DNS Bytes Out',
defaultMessage: 'DNS bytes out',
});
export const INCLUDE_PTR_RECORDS = i18n.translate(
'xpack.siem.networkDnsTable.select.includePtrRecords',
{
defaultMessage: 'Include PTR Records',
defaultMessage: 'Include PTR records',
}
);

View file

@ -7,7 +7,7 @@
import { i18n } from '@kbn/i18n';
export const TOP_TALKERS = i18n.translate('xpack.siem.networkTopNFlowTable.title', {
defaultMessage: 'Top Talkers',
defaultMessage: 'Top talkers',
});
export const UNIT = (totalCount: number) =>
@ -36,7 +36,7 @@ export const SERVER_IP = i18n.translate('xpack.siem.networkTopNFlowTable.column.
});
export const DOMAIN = i18n.translate('xpack.siem.networkTopNFlowTable.column.lastDomainTitle', {
defaultMessage: 'Last Domain',
defaultMessage: 'Last domain',
});
export const BYTES = i18n.translate('xpack.siem.networkTopNFlowTable.column.bytesTitle', {
@ -54,63 +54,59 @@ export const DIRECTION = i18n.translate('xpack.siem.networkTopNFlowTable.column.
export const UNIQUE_SOURCE_IP = i18n.translate(
'xpack.siem.networkTopNFlowTable.column.uniqueSourceIpsTitle',
{
defaultMessage: 'Unique Source IPs',
defaultMessage: 'Unique source IPs',
}
);
export const UNIQUE_DESTINATION_IP = i18n.translate(
'xpack.siem.networkTopNFlowTable.column.uniqueDestinationIpsTitle',
{
defaultMessage: 'Unique Destination IPs',
defaultMessage: 'Unique destination IPs',
}
);
export const UNIQUE_CLIENT_IP = i18n.translate(
'xpack.siem.networkTopNFlowTable.column.uniqueClientIpsTitle',
{
defaultMessage: 'Unique Client IPs',
defaultMessage: 'Unique client IPs',
}
);
export const UNIQUE_SERVER_IP = i18n.translate(
'xpack.siem.networkTopNFlowTable.column.uniqueServerIpsTitle',
{
defaultMessage: 'Unique Server IPs',
defaultMessage: 'Unique server IPs',
}
);
export const BY_SOURCE_IP = i18n.translate(
'xpack.siem.networkTopNFlowTable.select.bySourceIpDropDownOptionLabel',
{
defaultMessage: 'By Source IP',
defaultMessage: 'By source IP',
}
);
export const BY_DESTINATION_IP = i18n.translate(
'xpack.siem.networkTopNFlowTable.select.byDestinationIpDropDownOptionLabel',
{
defaultMessage: 'By Destination IP',
defaultMessage: 'By destination IP',
}
);
export const BY_CLIENT_IP = i18n.translate(
'xpack.siem.networkTopNFlowTable.select.byClientIpDropDownOptionLabel',
{
defaultMessage: 'By Client IP',
defaultMessage: 'By client IP',
}
);
export const BY_SERVER_IP = i18n.translate(
'xpack.siem.networkTopNFlowTable.select.byServerIpDropDownOptionLabel',
{
defaultMessage: 'By Server IP',
defaultMessage: 'By server IP',
}
);
export const MORE = i18n.translate('xpack.siem.networkTopNFlowTable.moreDescription', {
defaultMessage: 'More ...',
});
export const ROWS_5 = i18n.translate('xpack.siem.networkTopNFlowTable.rows', {
values: { numRows: 5 },
defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}',

View file

@ -88,7 +88,7 @@ describe('Tls Table Component', () => {
.find('.euiTable thead tr th button')
.first()
.text()
).toEqual('SHA1 FingerprintClick to sort in descending order');
).toEqual('SHA1 fingerprintClick to sort in descending order');
});
});
});

View file

@ -9,14 +9,14 @@ import { i18n } from '@kbn/i18n';
export const TRANSPORT_LAYER_SECURITY = i18n.translate(
'xpack.siem.network.ipDetails.tlsTable.transportLayerSecurityTitle',
{
defaultMessage: 'Transport Layer Security',
defaultMessage: 'Transport layer security',
}
);
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.network.ipDetails.tlsTable.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {Issuer} other {Issuers}}`,
defaultMessage: `{totalCount, plural, =1 {issuer} other {issuers}}`,
});
// Columns
@ -34,21 +34,21 @@ export const SUBJECT = i18n.translate(
export const SHA1_FINGERPRINT = i18n.translate(
'xpack.siem.network.ipDetails.tlsTable.columns.sha1FingerPrintTitle',
{
defaultMessage: 'SHA1 Fingerprint',
defaultMessage: 'SHA1 fingerprint',
}
);
export const JA3_FINGERPRINT = i18n.translate(
'xpack.siem.network.ipDetails.tlsTable.columns.ja3FingerPrintTitle',
{
defaultMessage: 'JA3 Fingerprint',
defaultMessage: 'JA3 fingerprint',
}
);
export const VALID_UNTIL = i18n.translate(
'xpack.siem.network.ipDetails.tlsTable.columns.validUntilTitle',
{
defaultMessage: 'Valid Until',
defaultMessage: 'Valid until',
}
);
@ -62,7 +62,3 @@ export const ROWS_10 = i18n.translate('xpack.siem.network.ipDetails.tlsTable.row
values: { numRows: 10 },
defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}',
});
export const MORE = i18n.translate('xpack.siem.network.ipDetails.tlsTable.moreDescription', {
defaultMessage: 'More ...',
});

View file

@ -13,7 +13,7 @@ export const USERS = i18n.translate('xpack.siem.network.ipDetails.usersTable.use
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.network.ipDetails.usersTable.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {User} other {Users}}`,
defaultMessage: `{totalCount, plural, =1 {user} other {users}}`,
});
// Columns
@ -34,7 +34,7 @@ export const USER_ID = i18n.translate(
export const GROUP_NAME = i18n.translate(
'xpack.siem.network.ipDetails.usersTable.columns.groupNameTitle',
{
defaultMessage: 'Group Name',
defaultMessage: 'Group name',
}
);
@ -48,7 +48,7 @@ export const GROUP_ID = i18n.translate(
export const DOCUMENT_COUNT = i18n.translate(
'xpack.siem.network.ipDetails.usersTable.columns.documentCountTitle',
{
defaultMessage: 'Document Count',
defaultMessage: 'Document count',
}
);
@ -62,7 +62,3 @@ export const ROWS_10 = i18n.translate('xpack.siem.network.ipDetails.usersTable.r
values: { numRows: 10 },
defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}',
});
export const MORE = i18n.translate('xpack.siem.network.ipDetails.usersTable.moreDescription', {
defaultMessage: 'More ...',
});

View file

@ -48,15 +48,15 @@ export const OverviewHost = pure<OverviewHostProps>(({ endDate, startDate, setQu
subtitle={
<FormattedMessage
id="xpack.siem.overview.hostsSubtitle"
defaultMessage="Showing: Last 24 Hours"
defaultMessage="Showing: Last 24 hours"
/>
}
title={
<FormattedMessage id="xpack.siem.overview.hostsTitle" defaultMessage="Host Events" />
<FormattedMessage id="xpack.siem.overview.hostsTitle" defaultMessage="Host events" />
}
>
<EuiButton href="#/link-to/hosts">
<FormattedMessage id="xpack.siem.overview.hostsAction" defaultMessage="View Hosts" />
<FormattedMessage id="xpack.siem.overview.hostsAction" defaultMessage="View hosts" />
</EuiButton>
</HeaderPanel>

View file

@ -48,20 +48,20 @@ export const OverviewNetwork = pure<OwnProps>(({ endDate, startDate, setQuery })
subtitle={
<FormattedMessage
id="xpack.siem.overview.networkSubtitle"
defaultMessage="Showing: Last 24 Hours"
defaultMessage="Showing: Last 24 hours"
/>
}
title={
<FormattedMessage
id="xpack.siem.overview.networkTitle"
defaultMessage="Network Events"
defaultMessage="Network events"
/>
}
>
<EuiButton href="#/link-to/network/">
<FormattedMessage
id="xpack.siem.overview.networkAction"
defaultMessage="View Network"
defaultMessage="View network"
/>
</EuiButton>
</HeaderPanel>

View file

@ -18,7 +18,7 @@ import {
} from '@elastic/eui';
import { noop } from 'lodash/fp';
import React, { memo, useState, useEffect } from 'react';
import styled from 'styled-components';
import styled, { css } from 'styled-components';
import { Direction } from '../../graphql/types';
import { AuthTableColumns } from '../page/hosts/authentications_table';
@ -253,36 +253,29 @@ export const PaginatedTable = memo<SiemTables>(
/>
<FooterAction>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
{itemsPerRow &&
itemsPerRow.length > 0 &&
totalCount >= itemsPerRow[0].numberOfRow && (
<EuiPopover
id="customizablePagination"
data-test-subj="loadingMoreSizeRowPopover"
button={button}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
>
<EuiContextMenuPanel
items={rowItems}
data-test-subj="loadingMorePickSizeRow"
/>
</EuiPopover>
)}
</EuiFlexItem>
<EuiFlexItem>
{itemsPerRow && itemsPerRow.length > 0 && totalCount >= itemsPerRow[0].numberOfRow && (
<EuiPopover
id="customizablePagination"
data-test-subj="loadingMoreSizeRowPopover"
button={button}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
>
<EuiContextMenuPanel items={rowItems} data-test-subj="loadingMorePickSizeRow" />
</EuiPopover>
)}
</EuiFlexItem>
<PaginationWrapper grow={false}>
<EuiPagination
data-test-subj="numberedPagination"
pageCount={pageCount}
activePage={activePage}
onPageClick={goToPage}
/>
</PaginationWrapper>
</EuiFlexGroup>
<PaginationWrapper grow={false}>
<EuiPagination
data-test-subj="numberedPagination"
pageCount={pageCount}
activePage={activePage}
onPageClick={goToPage}
/>
</PaginationWrapper>
</FooterAction>
{loading && <Loader data-test-subj="loadingPanelPaginatedTable" overlay size="xl" />}
@ -312,23 +305,33 @@ const BasicTable = styled(EuiBasicTable)`
}
`;
const FooterAction = styled.div`
margin-top: ${props => props.theme.eui.euiSize};
const FooterAction = styled(EuiFlexGroup).attrs({
alignItems: 'center',
responsive: false,
})`
margin-top: ${props => props.theme.eui.euiSizeXS};
`;
const PaginationEuiFlexItem = styled(EuiFlexItem)`
button.euiButtonIcon.euiButtonIcon--text {
margin-left: 20px;
}
.euiPagination {
position: relative;
}
.euiPagination::before {
content: '\\2026';
bottom: 5px;
color: ${props => props.theme.eui.euiButtonColorDisabled};
font-size: ${props => props.theme.eui.euiFontSizeS};
position: absolute;
right: 30px;
}
${props => css`
@media only screen and (min-width: ${props.theme.eui.euiBreakpoints.m}) {
.euiButtonIcon:last-child {
margin-left: 28px;
}
.euiPagination {
position: relative;
}
.euiPagination::before {
bottom: 0;
color: ${props.theme.eui.euiButtonColorDisabled};
content: '\\2026';
font-size: ${props.theme.eui.euiFontSizeS};
padding: 5px ${props.theme.eui.euiSizeS};
position: absolute;
right: ${props.theme.eui.euiSizeL};
}
}
`}
`;

View file

@ -255,8 +255,9 @@ export class Footer extends React.PureComponent<FooterProps, FooterState> {
{isLive ? (
<EuiText size="s" data-test-subj="is-live-on-message">
<b>
{i18n.AUTO_REFRESH_ACTIVE}
{i18n.AUTO_REFRESH_ACTIVE}{' '}
<EuiIconTip
color="subdued"
content={
<FormattedMessage
id="xpack.siem.footer.autoRefreshActiveTooltip"
@ -266,7 +267,7 @@ export class Footer extends React.PureComponent<FooterProps, FooterState> {
}}
/>
}
position="top"
type="iInCircle"
/>
</b>
</EuiText>

View file

@ -14,7 +14,7 @@ export const Summary = pure(() => (
<EuiFlexItem>
<EuiText>
<h2>
<FormattedMessage id="xpack.siem.overview.startedTitle" defaultMessage="Getting Started" />
<FormattedMessage id="xpack.siem.overview.startedTitle" defaultMessage="Getting started" />
</h2>
<p>
@ -42,7 +42,7 @@ export const Summary = pure(() => (
<EuiLink href="https://www.elastic.co/solutions/siem" target="blank">
<FormattedMessage
id="xpack.siem.overview.startedText.siemSolutionLinkText"
defaultMessage="SIEM Solution"
defaultMessage="SIEM solution"
/>
</EuiLink>
),

View file

@ -13,6 +13,6 @@ export const PAGE_TITLE = i18n.translate('xpack.siem.timelines.pageTitle', {
export const ALL_TIMELINES_PANEL_TITLE = i18n.translate(
'xpack.siem.timelines.allTimelines.panelTitle',
{
defaultMessage: 'All Timelines',
defaultMessage: 'All timelines',
}
);

View file

@ -9569,7 +9569,6 @@
"xpack.siem.hosts.kqlPlaceholder": "例: host.name: \"foo\"",
"xpack.siem.hosts.pageTitle": "すべてのホスト",
"xpack.siem.hostsTable.firstLastSeenToolTip": "選択された日付範囲との相関付けです",
"xpack.siem.hostsTable.firstSeenTitle": "初回の表示",
"xpack.siem.hostsTable.hostsTitle": "すべてのホスト",
"xpack.siem.hostsTable.lastSeenTitle": "前回の表示",
"xpack.siem.hostsTable.nameTitle": "名前",
@ -9578,25 +9577,6 @@
"xpack.siem.hostsTable.unit": "{totalCount, plural, =1 {Host} other {Hosts}}",
"xpack.siem.hostsTable.versionTitle": "バージョン",
"xpack.siem.ja3.fingerprint.ja3.fingerprintLabel": "ja3",
"xpack.siem.kpiHosts.source.activeUsersTitle": "アクティブユーザー",
"xpack.siem.kpiHosts.source.agentsTitle": "エージェント",
"xpack.siem.kpiHosts.source.authenticationFailureTitle": "失敗",
"xpack.siem.kpiHosts.source.authenticationSuccessTitle": "成功",
"xpack.siem.kpiHosts.source.authenticationTitle": "ユーザー認証",
"xpack.siem.kpiHosts.source.hostsTitle": "すべてのホスト",
"xpack.siem.kpiHosts.source.uniqueDestinationIpsTitle": "送信先",
"xpack.siem.kpiHosts.source.uniqueIpsTitle": "固有の IP",
"xpack.siem.kpiHosts.source.uniqueSourceIpsAbbreviationTitle": "Src.",
"xpack.siem.kpiHosts.source.uniqueSourceIpsTitle": "送信元",
"xpack.siem.kpiNetwork.source.activeAgentsTitle": "アクティブなエージェント",
"xpack.siem.kpiNetwork.source.destinationTitle": "送信先",
"xpack.siem.kpiNetwork.source.dnsQueriesTitle": "DNSクエリ",
"xpack.siem.kpiNetwork.source.loadingDescription": "読み込み中",
"xpack.siem.kpiNetwork.source.networkEventsTitle": "ネットワークイベント",
"xpack.siem.kpiNetwork.source.sourceTitle": "送信元",
"xpack.siem.kpiNetwork.source.tlsHandshakesTitle": "TLSハンドシェイク",
"xpack.siem.kpiNetwork.source.uniqueFlowIdsTitle": "固有のフロー ID",
"xpack.siem.kpiNetwork.source.uniquePrivateIpsTitle": "固有のプライベート ID",
"xpack.siem.linkSecurityDescription": "SIEM アプリを閲覧します",
"xpack.siem.markdown.hint.boldLabel": "**太字**",
"xpack.siem.markdown.hint.bulletLabel": "* ビュレット",
@ -9615,12 +9595,10 @@
"xpack.siem.network.emptyActionPrimary": "セットアップの手順を表示",
"xpack.siem.network.emptyActionSecondary": "ドキュメントに移動",
"xpack.siem.network.emptyTitle": "SIEM アプリケーションのネットワークに関連したインデックスがないようです",
"xpack.siem.network.ipDetails.domainsTable.bidirectionalDropDownOptionLabel": "双方向",
"xpack.siem.network.ipDetails.domainsTable.columns.bytesTitle": "バイト",
"xpack.siem.network.ipDetails.domainsTable.columns.directionTitle": "方向",
"xpack.siem.network.ipDetails.domainsTable.columns.domainNameTitle": "ドメイン名",
"xpack.siem.network.ipDetails.domainsTable.columns.firstLastSeenToolTip": "選択された日付範囲との相関付けです",
"xpack.siem.network.ipDetails.domainsTable.columns.firstSeenTitle": "初回の表示",
"xpack.siem.network.ipDetails.domainsTable.columns.lastSeenTitle": "前回の表示",
"xpack.siem.network.ipDetails.domainsTable.columns.packetsTitle": "パケット",
"xpack.siem.network.ipDetails.domainsTable.columns.uniqueClientsTitle": "固有のサーバー",
@ -9628,22 +9606,15 @@
"xpack.siem.network.ipDetails.domainsTable.columns.uniqueServersTitle": "固有のクライアント",
"xpack.siem.network.ipDetails.domainsTable.columns.uniqueSourcesTitle": "固有の送信元",
"xpack.siem.network.ipDetails.domainsTable.domainsTitle": "ドメイン",
"xpack.siem.network.ipDetails.domainsTable.moreDescription": "他...",
"xpack.siem.network.ipDetails.domainsTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}",
"xpack.siem.network.ipDetails.domainsTable.unidirectionalDropDownOptionLabel": "一方向",
"xpack.siem.network.ipDetails.domainsTable.unit": "{totalCount, plural, =1 {Domain} other {Domains}}",
"xpack.siem.network.ipDetails.ipOverview.asDestinationDropDownOptionLabel": "送信先として",
"xpack.siem.network.ipDetails.ipOverview.asSourceDropDownOptionLabel": "送信元として",
"xpack.siem.network.ipDetails.ipOverview.autonomousSystemTitle": "自動システム",
"xpack.siem.network.ipDetails.ipOverview.firstSeenTitle": "初回の表示",
"xpack.siem.network.ipDetails.ipOverview.hostIdTitle": "ホスト ID",
"xpack.siem.network.ipDetails.ipOverview.hostNameTitle": "ホスト名",
"xpack.siem.network.ipDetails.ipOverview.ipReputationTitle": "評判",
"xpack.siem.network.ipDetails.ipOverview.lastBeatTitle": "前回のBeat",
"xpack.siem.network.ipDetails.ipOverview.lastSeenTitle": "最後の検知",
"xpack.siem.network.ipDetails.ipOverview.locationTitle": "場所",
"xpack.siem.network.ipDetails.ipOverview.viewTalosIntelligenceTitle": "talosIntelligence.com",
"xpack.siem.network.ipDetails.ipOverview.viewVirusTotalTitle.": "virustotal.com",
"xpack.siem.network.ipDetails.ipOverview.viewWhoisTitle": "iana.org",
"xpack.siem.network.ipDetails.ipOverview.whoIsTitle": "WhoIs",
"xpack.siem.network.ipDetails.tlsTable.columns.issuerTitle": "発行者",
@ -9651,7 +9622,6 @@
"xpack.siem.network.ipDetails.tlsTable.columns.sha1FingerPrintTitle": "SHA1 フィンガープリント",
"xpack.siem.network.ipDetails.tlsTable.columns.subjectTitle": "件名",
"xpack.siem.network.ipDetails.tlsTable.columns.validUntilTitle": "有効期限",
"xpack.siem.network.ipDetails.tlsTable.moreDescription": "他...",
"xpack.siem.network.ipDetails.tlsTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}",
"xpack.siem.network.ipDetails.tlsTable.transportLayerSecurityTitle": "Transport Layer Security",
"xpack.siem.network.ipDetails.tlsTable.unit": "{totalCount, plural, =1 {Issuer} other {Issuers}}",
@ -9660,7 +9630,6 @@
"xpack.siem.network.ipDetails.usersTable.columns.groupNameTitle": "グループ名",
"xpack.siem.network.ipDetails.usersTable.columns.userIdTitle": "ID",
"xpack.siem.network.ipDetails.usersTable.columns.userNameTitle": "名前",
"xpack.siem.network.ipDetails.usersTable.moreDescription": "他...",
"xpack.siem.network.ipDetails.usersTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}",
"xpack.siem.network.ipDetails.usersTable.unit": "{totalCount, plural, =1 {User} other {Users}}",
"xpack.siem.network.ipDetails.usersTable.usersTitle": "ユーザー",
@ -9688,7 +9657,6 @@
"xpack.siem.networkTopNFlowTable.column.uniqueDestinationIpsTitle": "固有の送信先IP",
"xpack.siem.networkTopNFlowTable.column.uniqueServerIpsTitle": "固有のサーバーIP",
"xpack.siem.networkTopNFlowTable.column.uniqueSourceIpsTitle": "固有の送信元 IP",
"xpack.siem.networkTopNFlowTable.moreDescription": "他...",
"xpack.siem.networkTopNFlowTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}",
"xpack.siem.networkTopNFlowTable.select.byClientIpDropDownOptionLabel": "クライアントIP",
"xpack.siem.networkTopNFlowTable.select.byDestinationIpDropDownOptionLabel": "送信先 IP 別",

View file

@ -9711,7 +9711,6 @@
"xpack.siem.hosts.kqlPlaceholder": "例如 host.name“foo”",
"xpack.siem.hosts.pageTitle": "主机",
"xpack.siem.hostsTable.firstLastSeenToolTip": "相对于选定日期范围",
"xpack.siem.hostsTable.firstSeenTitle": "首次看到时间",
"xpack.siem.hostsTable.hostsTitle": "所有主机",
"xpack.siem.hostsTable.lastSeenTitle": "最后看到时间",
"xpack.siem.hostsTable.nameTitle": "名称",
@ -9720,25 +9719,6 @@
"xpack.siem.hostsTable.unit": "{totalCount, plural, =1 {Host} other {Hosts}}",
"xpack.siem.hostsTable.versionTitle": "版本",
"xpack.siem.ja3.fingerprint.ja3.fingerprintLabel": "ja3",
"xpack.siem.kpiHosts.source.activeUsersTitle": "活动用户",
"xpack.siem.kpiHosts.source.agentsTitle": "代理",
"xpack.siem.kpiHosts.source.authenticationFailureTitle": "失败",
"xpack.siem.kpiHosts.source.authenticationSuccessTitle": "成功",
"xpack.siem.kpiHosts.source.authenticationTitle": "用户身份验证",
"xpack.siem.kpiHosts.source.hostsTitle": "主机",
"xpack.siem.kpiHosts.source.uniqueDestinationIpsTitle": "目标",
"xpack.siem.kpiHosts.source.uniqueIpsTitle": "唯一 IP",
"xpack.siem.kpiHosts.source.uniqueSourceIpsAbbreviationTitle": "源",
"xpack.siem.kpiHosts.source.uniqueSourceIpsTitle": "源",
"xpack.siem.kpiNetwork.source.activeAgentsTitle": "活动代理",
"xpack.siem.kpiNetwork.source.destinationTitle": "目标",
"xpack.siem.kpiNetwork.source.dnsQueriesTitle": "DNS 查询",
"xpack.siem.kpiNetwork.source.loadingDescription": "正在加载",
"xpack.siem.kpiNetwork.source.networkEventsTitle": "网络事件",
"xpack.siem.kpiNetwork.source.sourceTitle": "源",
"xpack.siem.kpiNetwork.source.tlsHandshakesTitle": "TLS 握手",
"xpack.siem.kpiNetwork.source.uniqueFlowIdsTitle": "唯一流 ID",
"xpack.siem.kpiNetwork.source.uniquePrivateIpsTitle": "唯一专用 IP",
"xpack.siem.linkSecurityDescription": "浏览您的 SIEM 应用",
"xpack.siem.markdown.hint.boldLabel": "**粗体**",
"xpack.siem.markdown.hint.bulletLabel": "* 项目符号",
@ -9757,12 +9737,10 @@
"xpack.siem.network.emptyActionPrimary": "查看设置说明",
"xpack.siem.network.emptyActionSecondary": "前往文档",
"xpack.siem.network.emptyTitle": "似乎您在 SIEM 应用程序中没有与网络相关的索引",
"xpack.siem.network.ipDetails.domainsTable.bidirectionalDropDownOptionLabel": "双向",
"xpack.siem.network.ipDetails.domainsTable.columns.bytesTitle": "字节",
"xpack.siem.network.ipDetails.domainsTable.columns.directionTitle": "方向",
"xpack.siem.network.ipDetails.domainsTable.columns.domainNameTitle": "域名",
"xpack.siem.network.ipDetails.domainsTable.columns.firstLastSeenToolTip": "相对于选定日期范围",
"xpack.siem.network.ipDetails.domainsTable.columns.firstSeenTitle": "首次看到时间",
"xpack.siem.network.ipDetails.domainsTable.columns.lastSeenTitle": "最后看到时间",
"xpack.siem.network.ipDetails.domainsTable.columns.packetsTitle": "数据包",
"xpack.siem.network.ipDetails.domainsTable.columns.uniqueClientsTitle": "唯一服务器",
@ -9770,22 +9748,15 @@
"xpack.siem.network.ipDetails.domainsTable.columns.uniqueServersTitle": "唯一客户端",
"xpack.siem.network.ipDetails.domainsTable.columns.uniqueSourcesTitle": "唯一源",
"xpack.siem.network.ipDetails.domainsTable.domainsTitle": "域",
"xpack.siem.network.ipDetails.domainsTable.moreDescription": "更多......",
"xpack.siem.network.ipDetails.domainsTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}",
"xpack.siem.network.ipDetails.domainsTable.unidirectionalDropDownOptionLabel": "单向",
"xpack.siem.network.ipDetails.domainsTable.unit": "{totalCount, plural, =1 {Domain} other {Domains}}",
"xpack.siem.network.ipDetails.ipOverview.asDestinationDropDownOptionLabel": "作为目标",
"xpack.siem.network.ipDetails.ipOverview.asSourceDropDownOptionLabel": "作为源",
"xpack.siem.network.ipDetails.ipOverview.autonomousSystemTitle": "自治系统",
"xpack.siem.network.ipDetails.ipOverview.firstSeenTitle": "首次看到时间",
"xpack.siem.network.ipDetails.ipOverview.hostIdTitle": "主机 ID",
"xpack.siem.network.ipDetails.ipOverview.hostNameTitle": "主机名",
"xpack.siem.network.ipDetails.ipOverview.ipReputationTitle": "信誉",
"xpack.siem.network.ipDetails.ipOverview.lastBeatTitle": "最后 Beat",
"xpack.siem.network.ipDetails.ipOverview.lastSeenTitle": "最后看到时间",
"xpack.siem.network.ipDetails.ipOverview.locationTitle": "位置",
"xpack.siem.network.ipDetails.ipOverview.viewTalosIntelligenceTitle": "talosIntelligence.com",
"xpack.siem.network.ipDetails.ipOverview.viewVirusTotalTitle.": "virustotal.com",
"xpack.siem.network.ipDetails.ipOverview.viewWhoisTitle": "iana.org",
"xpack.siem.network.ipDetails.ipOverview.whoIsTitle": "WhoIs",
"xpack.siem.network.ipDetails.tlsTable.columns.issuerTitle": "颁发者",
@ -9793,7 +9764,6 @@
"xpack.siem.network.ipDetails.tlsTable.columns.sha1FingerPrintTitle": "SHA1 指纹",
"xpack.siem.network.ipDetails.tlsTable.columns.subjectTitle": "主题",
"xpack.siem.network.ipDetails.tlsTable.columns.validUntilTitle": "失效日期",
"xpack.siem.network.ipDetails.tlsTable.moreDescription": "更多......",
"xpack.siem.network.ipDetails.tlsTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}",
"xpack.siem.network.ipDetails.tlsTable.transportLayerSecurityTitle": "传输层安全",
"xpack.siem.network.ipDetails.tlsTable.unit": "{totalCount, plural, =1 {Issuer} other {Issuers}}",
@ -9802,7 +9772,6 @@
"xpack.siem.network.ipDetails.usersTable.columns.groupNameTitle": "组名称",
"xpack.siem.network.ipDetails.usersTable.columns.userIdTitle": "ID",
"xpack.siem.network.ipDetails.usersTable.columns.userNameTitle": "名称",
"xpack.siem.network.ipDetails.usersTable.moreDescription": "更多......",
"xpack.siem.network.ipDetails.usersTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}",
"xpack.siem.network.ipDetails.usersTable.unit": "{totalCount, plural, =1 {User} other {Users}}",
"xpack.siem.network.ipDetails.usersTable.usersTitle": "用户",
@ -9830,7 +9799,6 @@
"xpack.siem.networkTopNFlowTable.column.uniqueDestinationIpsTitle": "唯一目标 IP",
"xpack.siem.networkTopNFlowTable.column.uniqueServerIpsTitle": "唯一服务器 IP",
"xpack.siem.networkTopNFlowTable.column.uniqueSourceIpsTitle": "唯一源 IP",
"xpack.siem.networkTopNFlowTable.moreDescription": "更多......",
"xpack.siem.networkTopNFlowTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}",
"xpack.siem.networkTopNFlowTable.select.byClientIpDropDownOptionLabel": "按客户端 IP",
"xpack.siem.networkTopNFlowTable.select.byDestinationIpDropDownOptionLabel": "按目标 IP",