mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[TIP] use indicators flyout in cases view (#145459)
- modify indicators flyout to hide filter in/out button - delete duplicate flyout component in cases module and use indicators flyout instead - add fields to query to fetch indicator by id - sort fields in flyout table views (overview and table tabs)
This commit is contained in:
parent
9e0da9802e
commit
f2532f3424
33 changed files with 827 additions and 828 deletions
|
@ -9,6 +9,7 @@ import React from 'react';
|
|||
import { Story } from '@storybook/react';
|
||||
import { of } from 'rxjs';
|
||||
import { IKibanaSearchResponse } from '@kbn/data-plugin/common';
|
||||
import { generateMockFileIndicator } from '../../../../../../common/types/indicator';
|
||||
import { CommentChildren } from './comment_children';
|
||||
import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers';
|
||||
import { AttachmentMetadata } from '../../../utils';
|
||||
|
@ -23,7 +24,6 @@ export const Default: Story<void> = () => {
|
|||
indicatorName: 'indicatorName',
|
||||
indicatorFeedName: 'indicatorFeedName',
|
||||
indicatorType: 'indicatorType',
|
||||
indicatorFirstSeen: 'indicatorFirstSeen',
|
||||
};
|
||||
|
||||
const response: IKibanaSearchResponse = {
|
||||
|
@ -31,12 +31,7 @@ export const Default: Story<void> = () => {
|
|||
isPartial: false,
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
prop1: 'prop1',
|
||||
prop2: 'prop2',
|
||||
},
|
||||
],
|
||||
hits: [generateMockFileIndicator()],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -61,7 +56,6 @@ export const Loading: Story<void> = () => {
|
|||
indicatorName: 'indicatorName',
|
||||
indicatorFeedName: 'indicatorFeedName',
|
||||
indicatorType: 'indicatorType',
|
||||
indicatorFirstSeen: 'indicatorFirstSeen',
|
||||
};
|
||||
|
||||
const response: IKibanaSearchResponse = {
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
import { AttachmentMetadata } from '../../../utils';
|
||||
import { TestProvidersComponent } from '../../../../../common/mocks/test_providers';
|
||||
import { useIndicatorById } from '../../../hooks';
|
||||
import { generateMockFileIndicator, Indicator } from '../../../../../../common/types/indicator';
|
||||
|
||||
jest.mock('../../../hooks/use_indicator_by_id');
|
||||
|
||||
|
@ -26,14 +27,10 @@ describe('attachment_children initComponent', () => {
|
|||
indicatorName: 'indicatorName',
|
||||
indicatorFeedName: 'indicatorFeedName',
|
||||
indicatorType: 'indicatorType',
|
||||
indicatorFirstSeen: 'indicatorFirstSeen',
|
||||
};
|
||||
|
||||
(useIndicatorById as jest.MockedFunction<typeof useIndicatorById>).mockReturnValue({
|
||||
indicator: {
|
||||
prop1: 'prop1',
|
||||
prop2: 'prop2',
|
||||
},
|
||||
indicator: generateMockFileIndicator(),
|
||||
isLoading: false,
|
||||
});
|
||||
|
||||
|
@ -53,11 +50,10 @@ describe('attachment_children initComponent', () => {
|
|||
indicatorName: 'indicatorName',
|
||||
indicatorFeedName: 'indicatorFeedName',
|
||||
indicatorType: 'indicatorType',
|
||||
indicatorFirstSeen: 'indicatorFirstSeen',
|
||||
};
|
||||
|
||||
(useIndicatorById as jest.MockedFunction<typeof useIndicatorById>).mockReturnValue({
|
||||
indicator: {},
|
||||
indicator: {} as Indicator,
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
import React, { useMemo, useState, VFC } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiLoadingLogo, EuiText } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { Indicator } from '../../../../../../common/types/indicator';
|
||||
import { IndicatorsFlyout } from '../../../../indicators/components/flyout';
|
||||
import { useStyles } from '../styles';
|
||||
import { useIndicatorById } from '../../../hooks';
|
||||
import { AttachmentMetadata } from '../../../utils';
|
||||
import { CasesFlyout } from '../flyout';
|
||||
|
||||
export const INDICATOR_NAME_TEST_ID = 'tiCasesIndicatorName';
|
||||
export const INDICATOR_FEED_NAME_TEST_ID = 'tiCasesIndicatorFeedName';
|
||||
|
@ -19,7 +20,7 @@ export const INDICATOR_TYPE_TEST_ID = 'tiCasesIndicatorTYPE';
|
|||
|
||||
export interface CommentChildrenProps {
|
||||
/**
|
||||
* Id of the document (indicator) to be fetched
|
||||
* Indicator's id of the indicator to fetch
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
|
@ -43,13 +44,13 @@ export const CommentChildren: VFC<CommentChildrenProps> = ({ id, metadata }) =>
|
|||
const flyoutFragment = useMemo(
|
||||
() =>
|
||||
expanded ? (
|
||||
<CasesFlyout
|
||||
metadata={metadata}
|
||||
rawDocument={indicator as Record<string, unknown>}
|
||||
<IndicatorsFlyout
|
||||
indicator={indicator as Indicator}
|
||||
closeFlyout={() => setExpanded(false)}
|
||||
kqlBarIntegration={true}
|
||||
/>
|
||||
) : null,
|
||||
[expanded, indicator, metadata]
|
||||
[expanded, indicator]
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
|
|
|
@ -1,206 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CasesFlyout should render flyout with json details 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<div
|
||||
data-eui="EuiFlyout"
|
||||
role="dialog"
|
||||
>
|
||||
<button
|
||||
aria-label="Close this dialog"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
type="button"
|
||||
/>
|
||||
<div
|
||||
class="euiFlyoutHeader emotion-euiFlyoutHeader-hasBorder"
|
||||
>
|
||||
<h2
|
||||
class="euiTitle emotion-euiTitle-m"
|
||||
data-test-subj="tiCasesFlyoutTitle"
|
||||
id="simpleFlyoutTitle_generated-id"
|
||||
>
|
||||
Indicator details
|
||||
</h2>
|
||||
<div
|
||||
class="euiSpacer euiSpacer--s emotion-euiSpacer-s"
|
||||
/>
|
||||
<div
|
||||
class="euiText emotion-euiText-xs"
|
||||
>
|
||||
<p
|
||||
data-test-subj="tiCasesFlyoutSubtitle"
|
||||
>
|
||||
First seen:
|
||||
Dec 31, 2021 @ 20:01:01.000
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="euiSpacer euiSpacer--m emotion-euiSpacer-m"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlyoutBody emotion-euiFlyoutBody"
|
||||
>
|
||||
<div
|
||||
class="euiFlyoutBody__overflow css-18yrfj9-noBanner"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="euiFlyoutBody__overflowContent"
|
||||
>
|
||||
<h2
|
||||
class="euiTitle emotion-euiTitle-m"
|
||||
>
|
||||
abc
|
||||
</h2>
|
||||
<div>
|
||||
<pre>
|
||||
<code
|
||||
data-code-language="json"
|
||||
data-test-subj="tiCasesFlyoutJsonCodeBlock"
|
||||
>
|
||||
{
|
||||
"prop1": "prop1",
|
||||
"prop2": "prop2"
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlyoutFooter emotion-euiFlyoutFooter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<div
|
||||
data-eui="EuiFlyout"
|
||||
role="dialog"
|
||||
>
|
||||
<button
|
||||
aria-label="Close this dialog"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
type="button"
|
||||
/>
|
||||
<div
|
||||
class="euiFlyoutHeader emotion-euiFlyoutHeader-hasBorder"
|
||||
>
|
||||
<h2
|
||||
class="euiTitle emotion-euiTitle-m"
|
||||
data-test-subj="tiCasesFlyoutTitle"
|
||||
id="simpleFlyoutTitle_generated-id"
|
||||
>
|
||||
Indicator details
|
||||
</h2>
|
||||
<div
|
||||
class="euiSpacer euiSpacer--s emotion-euiSpacer-s"
|
||||
/>
|
||||
<div
|
||||
class="euiText emotion-euiText-xs"
|
||||
>
|
||||
<p
|
||||
data-test-subj="tiCasesFlyoutSubtitle"
|
||||
>
|
||||
First seen:
|
||||
Dec 31, 2021 @ 20:01:01.000
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="euiSpacer euiSpacer--m emotion-euiSpacer-m"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlyoutBody emotion-euiFlyoutBody"
|
||||
>
|
||||
<div
|
||||
class="euiFlyoutBody__overflow css-18yrfj9-noBanner"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="euiFlyoutBody__overflowContent"
|
||||
>
|
||||
<h2
|
||||
class="euiTitle emotion-euiTitle-m"
|
||||
>
|
||||
abc
|
||||
</h2>
|
||||
<div>
|
||||
<pre>
|
||||
<code
|
||||
data-code-language="json"
|
||||
data-test-subj="tiCasesFlyoutJsonCodeBlock"
|
||||
>
|
||||
{
|
||||
"prop1": "prop1",
|
||||
"prop2": "prop2"
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlyoutFooter emotion-euiFlyoutFooter"
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Story } from '@storybook/react';
|
||||
import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers';
|
||||
import { AttachmentMetadata } from '../../../utils';
|
||||
import { CasesFlyout } from './flyout';
|
||||
|
||||
export default {
|
||||
title: 'CasesFlyout',
|
||||
};
|
||||
|
||||
export const Default: Story<void> = () => {
|
||||
const metadata: AttachmentMetadata = {
|
||||
indicatorName: 'indicatorName',
|
||||
indicatorFeedName: 'indicatorFeedName',
|
||||
indicatorType: 'indicatorType',
|
||||
indicatorFirstSeen: 'indicatorFirstSeen',
|
||||
};
|
||||
const rawDocument: Record<string, unknown> = {
|
||||
prop1: 'prop1',
|
||||
prop2: 'prop2',
|
||||
};
|
||||
const closeFlyout = () => window.alert('Closing flyout');
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<CasesFlyout metadata={metadata} rawDocument={rawDocument} closeFlyout={closeFlyout} />
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export const EmptyRawDocument: Story<void> = () => {
|
||||
const metadata: AttachmentMetadata = {
|
||||
indicatorName: 'indicatorName',
|
||||
indicatorFeedName: 'indicatorFeedName',
|
||||
indicatorType: 'indicatorType',
|
||||
indicatorFirstSeen: 'indicatorFirstSeen',
|
||||
};
|
||||
const rawDocument = null as unknown as Record<string, unknown>;
|
||||
const closeFlyout = () => window.alert('Closing flyout');
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<CasesFlyout metadata={metadata} rawDocument={rawDocument} closeFlyout={closeFlyout} />
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { AttachmentMetadata } from '../../../utils';
|
||||
import { TestProvidersComponent } from '../../../../../common/mocks/test_providers';
|
||||
import { CasesFlyout } from './flyout';
|
||||
|
||||
describe('CasesFlyout', () => {
|
||||
it('should render flyout with json details', () => {
|
||||
const metadata: AttachmentMetadata = {
|
||||
indicatorName: 'abc',
|
||||
indicatorType: 'file',
|
||||
indicatorFeedName: 'feed',
|
||||
indicatorFirstSeen: '2022-01-01T01:01:01.000Z',
|
||||
};
|
||||
const rawDocument: Record<string, unknown> = {
|
||||
prop1: 'prop1',
|
||||
prop2: 'prop2',
|
||||
};
|
||||
const closeFlyout = () => window.alert('closing');
|
||||
const component = render(
|
||||
<TestProvidersComponent>
|
||||
<CasesFlyout metadata={metadata} rawDocument={rawDocument} closeFlyout={closeFlyout} />
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { VFC } from 'react';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutFooter,
|
||||
EuiFlyoutHeader,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
useGeneratedHtmlId,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { AttachmentMetadata } from '../../../utils';
|
||||
import { DateFormatter } from '../../../../../components/date_formatter/date_formatter';
|
||||
import { CasesFlyoutJson } from '../json';
|
||||
|
||||
export const TITLE_TEST_ID = 'tiCasesFlyoutTitle';
|
||||
export const SUBTITLE_TEST_ID = 'tiCasesFlyoutSubtitle';
|
||||
|
||||
export interface CasesFlyoutProps {
|
||||
/**
|
||||
* Metadata saved in the case attachment (indicator)
|
||||
*/
|
||||
metadata: AttachmentMetadata;
|
||||
/**
|
||||
* Document (indicator) retrieve by id
|
||||
*/
|
||||
rawDocument: Record<string, unknown>;
|
||||
/**
|
||||
* Event to close flyout (used by {@link EuiFlyout}).
|
||||
*/
|
||||
closeFlyout: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Leverages the {@link EuiFlyout} from the @elastic/eui library to dhow the details of a specific {@link Indicator}.
|
||||
*/
|
||||
export const CasesFlyout: VFC<CasesFlyoutProps> = ({ metadata, rawDocument, closeFlyout }) => {
|
||||
const flyoutTitleId = useGeneratedHtmlId({
|
||||
prefix: 'simpleFlyoutTitle',
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiFlyout onClose={closeFlyout} aria-labelledby={flyoutTitleId}>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle>
|
||||
<h2 data-test-subj={TITLE_TEST_ID} id={flyoutTitleId}>
|
||||
<FormattedMessage
|
||||
id="xpack.threatIntelligence.cases.flyout.title"
|
||||
defaultMessage="Indicator details"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size={'xs'}>
|
||||
<p data-test-subj={SUBTITLE_TEST_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.threatIntelligence.cases.flyout.subTitle"
|
||||
defaultMessage="First seen: "
|
||||
/>
|
||||
<DateFormatter date={metadata.indicatorFirstSeen} />
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<CasesFlyoutJson rawDocument={rawDocument} metadata={metadata} />
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter />
|
||||
</EuiFlyout>
|
||||
);
|
||||
};
|
|
@ -1,243 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CasesFlyoutJsonProps should render error if input is null 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<div
|
||||
class="euiPanel euiPanel--danger euiEmptyPrompt euiEmptyPrompt--vertical euiEmptyPrompt--paddingLarge emotion-euiPanel-m-danger"
|
||||
data-test-subj="indicatorEmptyPrompt"
|
||||
>
|
||||
<div
|
||||
class="euiEmptyPrompt__main"
|
||||
>
|
||||
<div
|
||||
class="euiEmptyPrompt__icon"
|
||||
>
|
||||
<span
|
||||
color="danger"
|
||||
data-euiicon-type="alert"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="euiEmptyPrompt__content"
|
||||
>
|
||||
<div
|
||||
class="euiEmptyPrompt__contentInner"
|
||||
>
|
||||
<h2
|
||||
class="euiTitle emotion-euiTitle-m"
|
||||
>
|
||||
Unable to display indicator information
|
||||
</h2>
|
||||
<div
|
||||
class="euiSpacer euiSpacer--m emotion-euiSpacer-m"
|
||||
/>
|
||||
<div
|
||||
class="euiText emotion-euiText-m-euiTextColor-subdued"
|
||||
>
|
||||
<p>
|
||||
There was an error displaying the indicator fields and values.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<div
|
||||
class="euiPanel euiPanel--danger euiEmptyPrompt euiEmptyPrompt--vertical euiEmptyPrompt--paddingLarge emotion-euiPanel-m-danger"
|
||||
data-test-subj="indicatorEmptyPrompt"
|
||||
>
|
||||
<div
|
||||
class="euiEmptyPrompt__main"
|
||||
>
|
||||
<div
|
||||
class="euiEmptyPrompt__icon"
|
||||
>
|
||||
<span
|
||||
color="danger"
|
||||
data-euiicon-type="alert"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="euiEmptyPrompt__content"
|
||||
>
|
||||
<div
|
||||
class="euiEmptyPrompt__contentInner"
|
||||
>
|
||||
<h2
|
||||
class="euiTitle emotion-euiTitle-m"
|
||||
>
|
||||
Unable to display indicator information
|
||||
</h2>
|
||||
<div
|
||||
class="euiSpacer euiSpacer--m emotion-euiSpacer-m"
|
||||
/>
|
||||
<div
|
||||
class="euiText emotion-euiText-m-euiTextColor-subdued"
|
||||
>
|
||||
<p>
|
||||
There was an error displaying the indicator fields and values.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CasesFlyoutJsonProps should render json 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<h2
|
||||
class="euiTitle emotion-euiTitle-m"
|
||||
>
|
||||
abc
|
||||
</h2>
|
||||
<div>
|
||||
<pre>
|
||||
<code
|
||||
data-code-language="json"
|
||||
data-test-subj="tiCasesFlyoutJsonCodeBlock"
|
||||
>
|
||||
{
|
||||
"prop1": "prop1",
|
||||
"prop2": "prop2"
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<h2
|
||||
class="euiTitle emotion-euiTitle-m"
|
||||
>
|
||||
abc
|
||||
</h2>
|
||||
<div>
|
||||
<pre>
|
||||
<code
|
||||
data-code-language="json"
|
||||
data-test-subj="tiCasesFlyoutJsonCodeBlock"
|
||||
>
|
||||
{
|
||||
"prop1": "prop1",
|
||||
"prop2": "prop2"
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
|
@ -1,8 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './json';
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Story } from '@storybook/react';
|
||||
import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers';
|
||||
import { CasesFlyoutJson } from './json';
|
||||
import { AttachmentMetadata } from '../../../utils';
|
||||
|
||||
export default {
|
||||
title: 'CasesFlyoutJson',
|
||||
};
|
||||
|
||||
export const Default: Story<void> = () => {
|
||||
const metadata: AttachmentMetadata = {
|
||||
indicatorName: 'indicatorName',
|
||||
indicatorFeedName: 'indicatorFeedName',
|
||||
indicatorType: 'indicatorType',
|
||||
indicatorFirstSeen: 'indicatorFirstSeen',
|
||||
};
|
||||
const rawDocument: Record<string, unknown> = {
|
||||
prop1: 'prop1',
|
||||
prop2: 'prop2',
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<CasesFlyoutJson metadata={metadata} rawDocument={rawDocument} />
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export const EmptyRawDocument: Story<void> = () => {
|
||||
const metadata: AttachmentMetadata = {
|
||||
indicatorName: 'indicatorName',
|
||||
indicatorFeedName: 'indicatorFeedName',
|
||||
indicatorType: 'indicatorType',
|
||||
indicatorFirstSeen: 'indicatorFirstSeen',
|
||||
};
|
||||
const rawDocument = null as unknown as Record<string, unknown>;
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<CasesFlyoutJson metadata={metadata} rawDocument={rawDocument} />
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
};
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { AttachmentMetadata } from '../../../utils';
|
||||
import { TestProvidersComponent } from '../../../../../common/mocks/test_providers';
|
||||
import { CasesFlyoutJson } from './json';
|
||||
|
||||
describe('CasesFlyoutJsonProps', () => {
|
||||
it('should render json', () => {
|
||||
const metadata: AttachmentMetadata = {
|
||||
indicatorName: 'abc',
|
||||
indicatorType: 'file',
|
||||
indicatorFeedName: 'feed',
|
||||
indicatorFirstSeen: '2022-01-01T01:01:01.000Z',
|
||||
};
|
||||
const rawDocument: Record<string, unknown> = {
|
||||
prop1: 'prop1',
|
||||
prop2: 'prop2',
|
||||
};
|
||||
const component = render(
|
||||
<TestProvidersComponent>
|
||||
<CasesFlyoutJson metadata={metadata} rawDocument={rawDocument} />
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render error if input is null', () => {
|
||||
const metadata: AttachmentMetadata = {
|
||||
indicatorName: 'abc',
|
||||
indicatorType: 'file',
|
||||
indicatorFeedName: 'feed',
|
||||
indicatorFirstSeen: '2022-01-01T01:01:01.000Z',
|
||||
};
|
||||
const rawDocument: Record<string, unknown> = null as unknown as Record<string, unknown>;
|
||||
const component = render(
|
||||
<TestProvidersComponent>
|
||||
<CasesFlyoutJson metadata={metadata} rawDocument={rawDocument} />
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { VFC } from 'react';
|
||||
import { EuiCodeBlock, EuiTitle } from '@elastic/eui';
|
||||
import { AttachmentMetadata } from '../../../utils';
|
||||
import { IndicatorEmptyPrompt } from '../../../../indicators/components/flyout/empty_prompt';
|
||||
|
||||
export const CODE_BLOCK_TEST_ID = 'tiCasesFlyoutJsonCodeBlock';
|
||||
|
||||
interface CasesFlyoutJsonProps {
|
||||
/**
|
||||
* Metadata saved in the case attachment (indicator)
|
||||
*/
|
||||
metadata: AttachmentMetadata;
|
||||
/**
|
||||
* Document (indicator) retrieve by id
|
||||
*/
|
||||
rawDocument: Record<string, unknown>;
|
||||
/**
|
||||
* Used for unit and e2e tests.
|
||||
*/
|
||||
['data-test-subj']?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays highlighted indicator values based on indicator type
|
||||
*/
|
||||
export const CasesFlyoutJson: VFC<CasesFlyoutJsonProps> = ({
|
||||
metadata,
|
||||
rawDocument,
|
||||
'data-test-subj': dataTestSubj,
|
||||
}) => {
|
||||
return !rawDocument || Object.keys(rawDocument).length === 0 ? (
|
||||
<IndicatorEmptyPrompt />
|
||||
) : (
|
||||
<>
|
||||
<EuiTitle>
|
||||
<h2>{metadata.indicatorName}</h2>
|
||||
</EuiTitle>
|
||||
<EuiCodeBlock language="json" lineNumbers data-test-subj={CODE_BLOCK_TEST_ID}>
|
||||
{JSON.stringify(rawDocument, null, 2)}
|
||||
</EuiCodeBlock>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -11,6 +11,7 @@ import {
|
|||
IKibanaSearchResponse,
|
||||
isCompleteResponse,
|
||||
} from '@kbn/data-plugin/common';
|
||||
import { Indicator } from '../../../../common/types/indicator';
|
||||
import { useKibana } from '../../../hooks';
|
||||
import type { RawIndicatorsResponse } from '../../indicators/services/fetch_indicators';
|
||||
|
||||
|
@ -25,7 +26,7 @@ export const useIndicatorById = (indicatorId: string) => {
|
|||
data: { search: searchService },
|
||||
},
|
||||
} = useKibana();
|
||||
const [indicator, setIndicator] = useState<Record<string, unknown>>();
|
||||
const [indicator, setIndicator] = useState<Indicator>();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -42,11 +43,18 @@ export const useIndicatorById = (indicatorId: string) => {
|
|||
],
|
||||
},
|
||||
};
|
||||
const fields = [
|
||||
{
|
||||
field: '*',
|
||||
include_unmapped: true,
|
||||
},
|
||||
];
|
||||
const req = {
|
||||
params: {
|
||||
index: ['filebeat-*'],
|
||||
body: {
|
||||
query,
|
||||
fields,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -20,7 +20,6 @@ describe('generateAttachmentsWithoutOwner', () => {
|
|||
indicatorName: 'indicatorName',
|
||||
indicatorType: 'file',
|
||||
indicatorFeedName: 'Filebeat] AbuseCH Malwar',
|
||||
indicatorFirstSeen: '2022-01-01T01:01:01.000Z',
|
||||
};
|
||||
|
||||
const result = generateAttachmentsWithoutOwner(externalReferenceId, metadata);
|
||||
|
@ -33,7 +32,6 @@ describe('generateAttachmentsWithoutOwner', () => {
|
|||
indicatorName: 'indicatorName',
|
||||
indicatorType: 'file',
|
||||
indicatorFeedName: 'Filebeat] AbuseCH Malwar',
|
||||
indicatorFirstSeen: '2022-01-01T01:01:01.000Z',
|
||||
};
|
||||
|
||||
const result: CaseAttachmentsWithoutOwner = generateAttachmentsWithoutOwner(
|
||||
|
|
|
@ -26,7 +26,6 @@ export interface AttachmentMetadata {
|
|||
indicatorName: string;
|
||||
indicatorType: string;
|
||||
indicatorFeedName: string;
|
||||
indicatorFirstSeen: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,15 +110,10 @@ export const generateAttachmentsMetadata = (indicator: Indicator): AttachmentMet
|
|||
indicator,
|
||||
RawIndicatorFieldId.Feed
|
||||
).value;
|
||||
const indicatorFirstSeen: string | null = getIndicatorFieldAndValue(
|
||||
indicator,
|
||||
RawIndicatorFieldId.FirstSeen
|
||||
).value;
|
||||
|
||||
return {
|
||||
indicatorName: indicatorName || EMPTY_VALUE,
|
||||
indicatorType: indicatorType || EMPTY_VALUE,
|
||||
indicatorFeedName: indicatorFeedName || EMPTY_VALUE,
|
||||
indicatorFirstSeen: indicatorFirstSeen || EMPTY_VALUE,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -223,3 +223,100 @@ Object {
|
|||
"unmount": [Function],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`<CopyToClipboardButtonEmpty /> <CopyToClipboardContextMenu /> should render one EuibuttonIcon 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="Copy to clipboard"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
data-test-subj="abc"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="copyClipboard"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="Copy to clipboard"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
data-test-subj="abc"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="copyClipboard"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -8,7 +8,11 @@
|
|||
import React from 'react';
|
||||
import { Story } from '@storybook/react';
|
||||
import { EuiContextMenuPanel } from '@elastic/eui';
|
||||
import { CopyToClipboardButtonEmpty, CopyToClipboardContextMenu } from '.';
|
||||
import {
|
||||
CopyToClipboardButtonEmpty,
|
||||
CopyToClipboardButtonIcon,
|
||||
CopyToClipboardContextMenu,
|
||||
} from '.';
|
||||
|
||||
export default {
|
||||
title: 'CopyToClipboard',
|
||||
|
@ -25,3 +29,7 @@ export const ContextMenu: Story<void> = () => {
|
|||
|
||||
return <EuiContextMenuPanel items={items} />;
|
||||
};
|
||||
|
||||
export const ButtonIcon: Story<void> = () => {
|
||||
return <CopyToClipboardButtonIcon value={mockValue} />;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { CopyToClipboardButtonEmpty, CopyToClipboardContextMenu } from '.';
|
||||
import {
|
||||
CopyToClipboardButtonEmpty,
|
||||
CopyToClipboardButtonIcon,
|
||||
CopyToClipboardContextMenu,
|
||||
} from '.';
|
||||
|
||||
const mockValue: string = 'Text copied';
|
||||
|
||||
|
@ -30,4 +34,12 @@ describe('<CopyToClipboardButtonEmpty /> <CopyToClipboardContextMenu />', () =>
|
|||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render one EuibuttonIcon', () => {
|
||||
const component = render(
|
||||
<CopyToClipboardButtonIcon value={mockValue} data-test-subj={mockTestId} />
|
||||
);
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React, { VFC } from 'react';
|
||||
import { EuiButtonEmpty, EuiContextMenuItem, EuiCopy } from '@elastic/eui';
|
||||
import { EuiButtonEmpty, EuiButtonIcon, EuiContextMenuItem, EuiCopy } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const COPY_ICON = 'copyClipboard';
|
||||
|
@ -80,3 +80,30 @@ export const CopyToClipboardContextMenu: VFC<CopyToClipboardProps> = ({
|
|||
)}
|
||||
</EuiCopy>
|
||||
);
|
||||
|
||||
/**
|
||||
* Takes a string and copies it to the clipboard.
|
||||
*
|
||||
* This component renders an {@link EuiButtonIcon}.
|
||||
*
|
||||
* @returns An EuiCopy element
|
||||
*/
|
||||
export const CopyToClipboardButtonIcon: VFC<CopyToClipboardProps> = ({
|
||||
value,
|
||||
'data-test-subj': dataTestSub,
|
||||
}) => (
|
||||
<EuiCopy textToCopy={value}>
|
||||
{(copy) => (
|
||||
<EuiButtonIcon
|
||||
aria-label={COPY_TITLE}
|
||||
iconType={COPY_ICON}
|
||||
iconSize="s"
|
||||
color="primary"
|
||||
onClick={copy}
|
||||
data-test-subj={dataTestSub}
|
||||
>
|
||||
{COPY_TITLE}
|
||||
</EuiButtonIcon>
|
||||
)}
|
||||
</EuiCopy>
|
||||
);
|
||||
|
|
|
@ -5,4 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './flyout';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export interface IndicatorsFlyoutContextValue {
|
||||
kqlBarIntegration: boolean;
|
||||
}
|
||||
|
||||
export const IndicatorsFlyoutContext = createContext<IndicatorsFlyoutContextValue | undefined>(
|
||||
undefined
|
||||
);
|
|
@ -11,6 +11,7 @@ import { IndicatorFieldsTable } from '.';
|
|||
import { generateMockIndicator } from '../../../../../../common/types/indicator';
|
||||
import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers';
|
||||
import { IndicatorsFiltersContext } from '../../../containers/filters';
|
||||
import { IndicatorsFlyoutContext } from '../context';
|
||||
|
||||
export default {
|
||||
component: IndicatorFieldsTable,
|
||||
|
@ -19,15 +20,41 @@ export default {
|
|||
|
||||
export function WithIndicators() {
|
||||
const indicator = generateMockIndicator();
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={mockIndicatorsFiltersContext}>
|
||||
<IndicatorFieldsTable
|
||||
fields={['threat.indicator.type']}
|
||||
indicator={indicator}
|
||||
search={false}
|
||||
/>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorFieldsTable
|
||||
fields={['threat.indicator.type']}
|
||||
indicator={indicator}
|
||||
search={false}
|
||||
/>
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
}
|
||||
|
||||
export function NoFilterButtons() {
|
||||
const indicator = generateMockIndicator();
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={mockIndicatorsFiltersContext}>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorFieldsTable
|
||||
fields={['threat.indicator.type']}
|
||||
indicator={indicator}
|
||||
search={false}
|
||||
/>
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
|
|
|
@ -65,7 +65,7 @@ export const IndicatorFieldsTable: VFC<IndicatorFieldsTableProps> = ({
|
|||
|
||||
return (
|
||||
<EuiInMemoryTable
|
||||
items={fields}
|
||||
items={fields.sort()}
|
||||
columns={columns}
|
||||
sorting={true}
|
||||
data-test-subj={dataTestSubj}
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
useGeneratedHtmlId,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { IndicatorsFlyoutContext } from './context';
|
||||
import { TakeAction } from './take_action/take_action';
|
||||
import { DateFormatter } from '../../../../components/date_formatter/date_formatter';
|
||||
import { Indicator, RawIndicatorFieldId } from '../../../../../common/types/indicator';
|
||||
|
@ -49,12 +50,21 @@ export interface IndicatorsFlyoutProps {
|
|||
* Event to close flyout (used by {@link EuiFlyout}).
|
||||
*/
|
||||
closeFlyout: () => void;
|
||||
/**
|
||||
* Boolean deciding if we show or hide the filter in/out feature in the flyout.
|
||||
* We should be showing the filter in and out buttons when the flyout is used in the cases view.
|
||||
*/
|
||||
kqlBarIntegration?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Leverages the {@link EuiFlyout} from the @elastic/eui library to dhow the details of a specific {@link Indicator}.
|
||||
*/
|
||||
export const IndicatorsFlyout: VFC<IndicatorsFlyoutProps> = ({ indicator, closeFlyout }) => {
|
||||
export const IndicatorsFlyout: VFC<IndicatorsFlyoutProps> = ({
|
||||
indicator,
|
||||
closeFlyout,
|
||||
kqlBarIntegration = false,
|
||||
}) => {
|
||||
const [selectedTabId, setSelectedTabId] = useState(TAB_IDS.overview);
|
||||
|
||||
const tabs = useMemo(
|
||||
|
@ -146,7 +156,11 @@ export const IndicatorsFlyout: VFC<IndicatorsFlyoutProps> = ({ indicator, closeF
|
|||
{renderTabs}
|
||||
</EuiTabs>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>{selectedTabContent}</EuiFlyoutBody>
|
||||
<EuiFlyoutBody>
|
||||
<IndicatorsFlyoutContext.Provider value={{ kqlBarIntegration }}>
|
||||
{selectedTabContent}
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
|
|
@ -0,0 +1,386 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`IndicatorValueActions should only render add to timeline and copy to clipboard 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<div
|
||||
class="euiFlexGroup emotion-euiFlexGroup-responsive-none-center-center-row"
|
||||
>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem emotion-euiFlexItem-grow-1"
|
||||
data-test-subj="undefinedTimelineButton"
|
||||
>
|
||||
<span
|
||||
data-test-subj="test-add-to-timeline"
|
||||
>
|
||||
Add To Timeline
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="Copy to clipboard"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
data-test-subj="undefinedCopyToClipboardButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="copyClipboard"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<div
|
||||
class="euiFlexGroup emotion-euiFlexGroup-responsive-none-center-center-row"
|
||||
>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem emotion-euiFlexItem-grow-1"
|
||||
data-test-subj="undefinedTimelineButton"
|
||||
>
|
||||
<span
|
||||
data-test-subj="test-add-to-timeline"
|
||||
>
|
||||
Add To Timeline
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="Copy to clipboard"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
data-test-subj="undefinedCopyToClipboardButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="copyClipboard"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`IndicatorValueActions should render filter in/out and dropdown for add to timeline and copy to clipboard 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<div
|
||||
class="euiFlexGroup emotion-euiFlexGroup-responsive-none-center-center-row"
|
||||
>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="Filter In"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
data-test-subj="undefinedFilterInButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="plusInCircle"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="Filter Out"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
data-test-subj="undefinedFilterOutButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="minusInCircle"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
<div
|
||||
class="euiPopover emotion-euiPopover"
|
||||
data-test-subj="undefinedPopoverButton"
|
||||
>
|
||||
<div
|
||||
class="euiPopover__anchor css-16vtueo-render"
|
||||
>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="More actions"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
style="height: 100%;"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="boxesHorizontal"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<div
|
||||
class="euiFlexGroup emotion-euiFlexGroup-responsive-none-center-center-row"
|
||||
>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="Filter In"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
data-test-subj="undefinedFilterInButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="plusInCircle"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="Filter Out"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
data-test-subj="undefinedFilterOutButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="minusInCircle"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
<div
|
||||
class="euiPopover emotion-euiPopover"
|
||||
data-test-subj="undefinedPopoverButton"
|
||||
>
|
||||
<div
|
||||
class="euiPopover__anchor css-16vtueo-render"
|
||||
>
|
||||
<span
|
||||
class="euiToolTipAnchor emotion-euiToolTipAnchor-inlineBlock"
|
||||
>
|
||||
<button
|
||||
aria-label="More actions"
|
||||
class="euiButtonIcon euiButtonIcon--xSmall emotion-euiButtonIcon-empty-primary-hoverStyles"
|
||||
style="height: 100%;"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiButtonIcon__icon"
|
||||
color="inherit"
|
||||
data-euiicon-type="boxesHorizontal"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`IndicatorValueActions should return null if field and value are invalid 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div />
|
||||
</body>,
|
||||
"container": <div />,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Story } from '@storybook/react';
|
||||
import { StoryProvidersComponent } from '../../../../../common/mocks/story_providers';
|
||||
import { generateMockFileIndicator, Indicator } from '../../../../../../common/types/indicator';
|
||||
import { IndicatorValueActions } from '.';
|
||||
import { IndicatorsFlyoutContext } from '../context';
|
||||
|
||||
export default {
|
||||
title: 'IndicatorValueActions',
|
||||
};
|
||||
|
||||
const indicator: Indicator = generateMockFileIndicator();
|
||||
const field: string = 'threat.indicator.name';
|
||||
|
||||
export const Default: Story<void> = () => {
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithoutFilterInOut: Story<void> = () => {
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { generateMockFileIndicator, Indicator } from '../../../../../../common/types/indicator';
|
||||
import { render } from '@testing-library/react';
|
||||
import { IndicatorValueActions } from './indicator_value_actions';
|
||||
import { IndicatorsFlyoutContext } from '../context';
|
||||
import { TestProvidersComponent } from '../../../../../common/mocks/test_providers';
|
||||
|
||||
describe('IndicatorValueActions', () => {
|
||||
const indicator: Indicator = generateMockFileIndicator();
|
||||
|
||||
it('should return null if field and value are invalid', () => {
|
||||
const field: string = 'invalid';
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
const component = render(
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should only render add to timeline and copy to clipboard', () => {
|
||||
const field: string = 'threat.indicator.name';
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
const component = render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render filter in/out and dropdown for add to timeline and copy to clipboard', () => {
|
||||
const field: string = 'threat.indicator.name';
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
const component = render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -14,11 +14,12 @@ import {
|
|||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useIndicatorsFlyoutContext } from '../use_context';
|
||||
import { Indicator } from '../../../../../../common/types/indicator';
|
||||
import { FilterInButtonIcon, FilterOutButtonIcon } from '../../../../query_bar';
|
||||
import { AddToTimelineContextMenu } from '../../../../timeline';
|
||||
import { AddToTimelineButtonIcon, AddToTimelineContextMenu } from '../../../../timeline';
|
||||
import { fieldAndValueValid, getIndicatorFieldAndValue } from '../../../utils';
|
||||
import { CopyToClipboardContextMenu } from '../../copy_to_clipboard';
|
||||
import { CopyToClipboardButtonIcon, CopyToClipboardContextMenu } from '../../copy_to_clipboard';
|
||||
|
||||
export const TIMELINE_BUTTON_TEST_ID = 'TimelineButton';
|
||||
export const FILTER_IN_BUTTON_TEST_ID = 'FilterInButton';
|
||||
|
@ -45,11 +46,21 @@ interface IndicatorValueActions {
|
|||
['data-test-subj']?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This component render a set of actions for the user.
|
||||
* Currently used in the indicators flyout (overview and table tabs).
|
||||
*
|
||||
* It gets a readOnly boolean from context, that drives what is displayed.
|
||||
* - in the cases view usage, we only display add to timeline and copy to clipboard.
|
||||
* - in the indicators table usave, we display all options
|
||||
*/
|
||||
export const IndicatorValueActions: VFC<IndicatorValueActions> = ({
|
||||
indicator,
|
||||
field,
|
||||
'data-test-subj': dataTestSubj,
|
||||
}) => {
|
||||
const { kqlBarIntegration } = useIndicatorsFlyoutContext();
|
||||
|
||||
const [isPopoverOpen, setPopover] = useState(false);
|
||||
|
||||
const { key, value } = getIndicatorFieldAndValue(indicator, field);
|
||||
|
@ -63,13 +74,22 @@ export const IndicatorValueActions: VFC<IndicatorValueActions> = ({
|
|||
const copyToClipboardTestId = `${dataTestSubj}${COPY_TO_CLIPBOARD_BUTTON_TEST_ID}`;
|
||||
const popoverTestId = `${dataTestSubj}${POPOVER_BUTTON_TEST_ID}`;
|
||||
|
||||
if (kqlBarIntegration) {
|
||||
return (
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center" gutterSize="none">
|
||||
<AddToTimelineButtonIcon data={indicator} field={field} data-test-subj={timelineTestId} />
|
||||
<CopyToClipboardButtonIcon value={value as string} data-test-subj={copyToClipboardTestId} />
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const popoverItems = [
|
||||
<AddToTimelineContextMenu data={indicator} field={field} data-test-subj={timelineTestId} />,
|
||||
<CopyToClipboardContextMenu value={value as string} data-test-subj={copyToClipboardTestId} />,
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center">
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center" gutterSize="none">
|
||||
<FilterInButtonIcon data={indicator} field={field} data-test-subj={filterInTestId} />
|
||||
<FilterOutButtonIcon data={indicator} field={field} data-test-subj={filterOutTestId} />
|
||||
<EuiPopover
|
||||
|
|
|
@ -10,6 +10,7 @@ import { IndicatorsFiltersContext } from '../../../../containers/filters';
|
|||
import { StoryProvidersComponent } from '../../../../../../common/mocks/story_providers';
|
||||
import { generateMockIndicator } from '../../../../../../../common/types/indicator';
|
||||
import { IndicatorBlock } from '.';
|
||||
import { IndicatorsFlyoutContext } from '../../context';
|
||||
|
||||
export default {
|
||||
component: IndicatorBlock,
|
||||
|
@ -20,11 +21,33 @@ const mockIndicator = generateMockIndicator();
|
|||
|
||||
export function Default() {
|
||||
const mockField = 'threat.indicator.ip';
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={{} as any}>
|
||||
<IndicatorBlock indicator={mockIndicator} field={mockField} />
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorBlock indicator={mockIndicator} field={mockField} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
}
|
||||
|
||||
export function NoFilterButtons() {
|
||||
const mockField = 'threat.indicator.ip';
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={{} as any}>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorBlock indicator={mockIndicator} field={mockField} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
|
|
|
@ -11,6 +11,7 @@ import { StoryProvidersComponent } from '../../../../../common/mocks/story_provi
|
|||
import { generateMockIndicator, Indicator } from '../../../../../../common/types/indicator';
|
||||
import { IndicatorsFlyoutOverview } from '.';
|
||||
import { IndicatorsFiltersContext } from '../../../containers/filters';
|
||||
import { IndicatorsFlyoutContext } from '../context';
|
||||
|
||||
export default {
|
||||
component: IndicatorsFlyoutOverview,
|
||||
|
@ -25,11 +26,16 @@ export default {
|
|||
|
||||
export const Default: Story<void> = () => {
|
||||
const mockIndicator: Indicator = generateMockIndicator();
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={{} as any}>
|
||||
<IndicatorsFlyoutOverview onViewAllFieldsInTable={() => {}} indicator={mockIndicator} />
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutOverview onViewAllFieldsInTable={() => {}} indicator={mockIndicator} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
</StoryProvidersComponent>
|
||||
);
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
TI_FLYOUT_OVERVIEW_TABLE,
|
||||
} from '.';
|
||||
import { EMPTY_PROMPT_TEST_ID } from '../empty_prompt';
|
||||
import { IndicatorsFlyoutContext } from '../context';
|
||||
|
||||
describe('<IndicatorsFlyoutOverview />', () => {
|
||||
describe('invalid indicator', () => {
|
||||
|
@ -33,12 +34,18 @@ describe('<IndicatorsFlyoutOverview />', () => {
|
|||
});
|
||||
|
||||
it('should render the highlighted blocks and table when valid indicator is passed', () => {
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutOverview
|
||||
onViewAllFieldsInTable={() => {}}
|
||||
indicator={generateMockIndicator()}
|
||||
/>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutOverview
|
||||
onViewAllFieldsInTable={() => {}}
|
||||
indicator={generateMockIndicator()}
|
||||
/>
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
|
||||
|
|
|
@ -15,15 +15,18 @@ import { mockKibanaTimelinesService } from '../../../../../common/mocks/mock_kib
|
|||
import { generateMockIndicator, Indicator } from '../../../../../../common/types/indicator';
|
||||
import { IndicatorsFlyoutTable } from '.';
|
||||
import { IndicatorsFiltersContext } from '../../../containers/filters';
|
||||
import { IndicatorsFlyoutContext } from '../context';
|
||||
|
||||
export default {
|
||||
component: IndicatorsFlyoutTable,
|
||||
title: 'IndicatorsFlyoutTable',
|
||||
};
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
export const Default: Story<void> = () => {
|
||||
const mockIndicator: Indicator = generateMockIndicator();
|
||||
|
||||
const KibanaReactContext = createKibanaReactContext({
|
||||
uiSettings: mockUiSettingsService(),
|
||||
timelines: mockKibanaTimelinesService,
|
||||
|
@ -32,12 +35,18 @@ export const Default: Story<void> = () => {
|
|||
return (
|
||||
<KibanaReactContext.Provider>
|
||||
<IndicatorsFiltersContext.Provider value={mockIndicatorsFiltersContext}>
|
||||
<IndicatorsFlyoutTable indicator={mockIndicator} />
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutTable indicator={mockIndicator} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
</KibanaReactContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const EmptyIndicator: Story<void> = () => {
|
||||
return <IndicatorsFlyoutTable indicator={{ fields: {} } as unknown as Indicator} />;
|
||||
return (
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutTable indicator={{ fields: {} } as unknown as Indicator} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -16,14 +16,21 @@ import {
|
|||
import { IndicatorsFlyoutTable, TABLE_TEST_ID } from '.';
|
||||
import { unwrapValue } from '../../../utils';
|
||||
import { EMPTY_PROMPT_TEST_ID } from '../empty_prompt';
|
||||
import { IndicatorsFlyoutContext } from '../context';
|
||||
|
||||
const mockIndicator: Indicator = generateMockIndicator();
|
||||
|
||||
describe('<IndicatorsFlyoutTable />', () => {
|
||||
it('should render fields and values in table', () => {
|
||||
const kqlBarIntegration = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
const { getByTestId, getByText, getAllByText } = render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutTable indicator={mockIndicator} />
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutTable indicator={mockIndicator} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useContext } from 'react';
|
||||
import { IndicatorsFlyoutContext, IndicatorsFlyoutContextValue } from './context';
|
||||
|
||||
/**
|
||||
* Hook to retrieve {@link IndicatorsFiltersContext} (contains FilterManager)
|
||||
*/
|
||||
export const useIndicatorsFlyoutContext = (): IndicatorsFlyoutContextValue => {
|
||||
const contextValue = useContext(IndicatorsFlyoutContext);
|
||||
|
||||
if (!contextValue) {
|
||||
throw new Error(
|
||||
'IndicatorsFlyoutContext can only be used within IndicatorsFlyoutContext provider'
|
||||
);
|
||||
}
|
||||
|
||||
return contextValue;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue