mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[TIP] display indicator name within cases flyout (#146744)
elastic/security-team#5475
This commit is contained in:
parent
00df93eb1d
commit
7022e56b2a
12 changed files with 99 additions and 35 deletions
|
@ -48,9 +48,10 @@ export const CommentChildren: VFC<CommentChildrenProps> = ({ id, metadata }) =>
|
|||
indicator={indicator as Indicator}
|
||||
closeFlyout={() => setExpanded(false)}
|
||||
kqlBarIntegration={true}
|
||||
indicatorName={indicatorName}
|
||||
/>
|
||||
) : null,
|
||||
[expanded, indicator]
|
||||
[expanded, indicator, indicatorName]
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { createContext } from 'react';
|
|||
|
||||
export interface IndicatorsFlyoutContextValue {
|
||||
kqlBarIntegration: boolean;
|
||||
indicatorName?: string | undefined;
|
||||
}
|
||||
|
||||
export const IndicatorsFlyoutContext = createContext<IndicatorsFlyoutContextValue | undefined>(
|
||||
|
|
|
@ -20,14 +20,14 @@ export default {
|
|||
|
||||
export function WithIndicators() {
|
||||
const indicator = generateMockIndicator();
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={mockIndicatorsFiltersContext}>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorFieldsTable
|
||||
fields={['threat.indicator.type']}
|
||||
indicator={indicator}
|
||||
|
@ -41,14 +41,14 @@ export function WithIndicators() {
|
|||
|
||||
export function NoFilterButtons() {
|
||||
const indicator = generateMockIndicator();
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={mockIndicatorsFiltersContext}>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorFieldsTable
|
||||
fields={['threat.indicator.type']}
|
||||
indicator={indicator}
|
||||
|
|
|
@ -55,6 +55,13 @@ export interface IndicatorsFlyoutProps {
|
|||
* We should be showing the filter in and out buttons when the flyout is used in the cases view.
|
||||
*/
|
||||
kqlBarIntegration?: boolean;
|
||||
/**
|
||||
* Name of the indicator, used only when the flyout is rendered in the Cases view.
|
||||
* Because the indicator name is a runtime field, when querying for the indicator from within
|
||||
* the Cases view, this logic is not ran. Therefore, passing the name to the flyout is an
|
||||
* easy (hopefully temporary) solution to display it within the flyout.
|
||||
*/
|
||||
indicatorName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,6 +71,7 @@ export const IndicatorsFlyout: VFC<IndicatorsFlyoutProps> = ({
|
|||
indicator,
|
||||
closeFlyout,
|
||||
kqlBarIntegration = false,
|
||||
indicatorName,
|
||||
}) => {
|
||||
const [selectedTabId, setSelectedTabId] = useState(TAB_IDS.overview);
|
||||
|
||||
|
@ -157,7 +165,7 @@ export const IndicatorsFlyout: VFC<IndicatorsFlyoutProps> = ({
|
|||
</EuiTabs>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<IndicatorsFlyoutContext.Provider value={{ kqlBarIntegration }}>
|
||||
<IndicatorsFlyoutContext.Provider value={{ kqlBarIntegration, indicatorName }}>
|
||||
{selectedTabContent}
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</EuiFlyoutBody>
|
||||
|
|
|
@ -20,12 +20,12 @@ const indicator: Indicator = generateMockFileIndicator();
|
|||
const field: string = 'threat.indicator.name';
|
||||
|
||||
export const Default: Story<void> = () => {
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</StoryProvidersComponent>
|
||||
|
@ -33,12 +33,12 @@ export const Default: Story<void> = () => {
|
|||
};
|
||||
|
||||
export const WithoutFilterInOut: Story<void> = () => {
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</StoryProvidersComponent>
|
||||
|
|
|
@ -17,11 +17,11 @@ describe('IndicatorValueActions', () => {
|
|||
|
||||
it('should return null if field and value are invalid', () => {
|
||||
const field: string = 'invalid';
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
const component = render(
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
);
|
||||
|
@ -30,12 +30,12 @@ describe('IndicatorValueActions', () => {
|
|||
|
||||
it('should only render add to timeline and copy to clipboard', () => {
|
||||
const field: string = 'threat.indicator.name';
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
const component = render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
|
@ -45,12 +45,12 @@ describe('IndicatorValueActions', () => {
|
|||
|
||||
it('should render filter in/out and dropdown for add to timeline and copy to clipboard', () => {
|
||||
const field: string = 'threat.indicator.name';
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
const component = render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorValueActions indicator={indicator} field={field} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
|
|
|
@ -21,14 +21,14 @@ const mockIndicator = generateMockIndicator();
|
|||
|
||||
export function Default() {
|
||||
const mockField = 'threat.indicator.ip';
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={{} as any}>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorBlock indicator={mockIndicator} field={mockField} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
|
@ -38,14 +38,14 @@ export function Default() {
|
|||
|
||||
export function NoFilterButtons() {
|
||||
const mockField = 'threat.indicator.ip';
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={{} as any}>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorBlock indicator={mockIndicator} field={mockField} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
|
|
|
@ -26,14 +26,14 @@ export default {
|
|||
|
||||
export const Default: Story<void> = () => {
|
||||
const mockIndicator: Indicator = generateMockIndicator();
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<StoryProvidersComponent>
|
||||
<IndicatorsFiltersContext.Provider value={{} as any}>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorsFlyoutOverview onViewAllFieldsInTable={() => {}} indicator={mockIndicator} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
IndicatorsFlyoutOverview,
|
||||
TI_FLYOUT_OVERVIEW_HIGH_LEVEL_BLOCKS,
|
||||
TI_FLYOUT_OVERVIEW_TABLE,
|
||||
TI_FLYOUT_OVERVIEW_TITLE,
|
||||
} from '.';
|
||||
import { EMPTY_PROMPT_TEST_ID } from '../empty_prompt';
|
||||
import { IndicatorsFlyoutContext } from '../context';
|
||||
|
@ -20,12 +21,18 @@ import { IndicatorsFlyoutContext } from '../context';
|
|||
describe('<IndicatorsFlyoutOverview />', () => {
|
||||
describe('invalid indicator', () => {
|
||||
it('should render error message on invalid indicator', () => {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutOverview
|
||||
onViewAllFieldsInTable={() => {}}
|
||||
indicator={{ fields: {} } as unknown as Indicator}
|
||||
/>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorsFlyoutOverview
|
||||
onViewAllFieldsInTable={() => {}}
|
||||
indicator={{ fields: {} } as unknown as Indicator}
|
||||
/>
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
|
||||
|
@ -34,13 +41,13 @@ describe('<IndicatorsFlyoutOverview />', () => {
|
|||
});
|
||||
|
||||
it('should render the highlighted blocks and table when valid indicator is passed', () => {
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorsFlyoutOverview
|
||||
onViewAllFieldsInTable={() => {}}
|
||||
indicator={generateMockIndicator()}
|
||||
|
@ -52,4 +59,44 @@ describe('<IndicatorsFlyoutOverview />', () => {
|
|||
expect(screen.queryByTestId(TI_FLYOUT_OVERVIEW_TABLE)).toBeInTheDocument();
|
||||
expect(screen.queryByTestId(TI_FLYOUT_OVERVIEW_HIGH_LEVEL_BLOCKS)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the indicator name value in the title', () => {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
const indicator: Indicator = generateMockIndicator();
|
||||
const indicatorName: string = (indicator.fields['threat.indicator.name'] as string[])[0];
|
||||
|
||||
render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorsFlyoutOverview onViewAllFieldsInTable={() => {}} indicator={indicator} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId(TI_FLYOUT_OVERVIEW_TITLE)?.innerHTML).toContain(indicatorName);
|
||||
});
|
||||
|
||||
it('should render the indicator name passed via context in the title', () => {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
indicatorName: '123',
|
||||
};
|
||||
|
||||
render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorsFlyoutOverview
|
||||
onViewAllFieldsInTable={() => {}}
|
||||
indicator={generateMockIndicator()}
|
||||
/>
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId(TI_FLYOUT_OVERVIEW_TITLE)?.innerHTML).toContain(
|
||||
context.indicatorName
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React, { useMemo, VFC } from 'react';
|
||||
import { useIndicatorsFlyoutContext } from '../use_context';
|
||||
import { EMPTY_VALUE } from '../../../../../common/constants';
|
||||
import { Indicator, RawIndicatorFieldId } from '../../../../../../common/types/indicator';
|
||||
import { unwrapValue } from '../../../utils';
|
||||
|
@ -30,6 +31,7 @@ const highLevelFields = [
|
|||
RawIndicatorFieldId.Confidence,
|
||||
];
|
||||
|
||||
export const TI_FLYOUT_OVERVIEW_TITLE = 'tiFlyoutOverviewTitle';
|
||||
export const TI_FLYOUT_OVERVIEW_TABLE = 'tiFlyoutOverviewTableRow';
|
||||
export const TI_FLYOUT_OVERVIEW_HIGH_LEVEL_BLOCKS = 'tiFlyoutOverviewHighLevelBlocks';
|
||||
|
||||
|
@ -42,6 +44,8 @@ export const IndicatorsFlyoutOverview: VFC<IndicatorsFlyoutOverviewProps> = ({
|
|||
indicator,
|
||||
onViewAllFieldsInTable,
|
||||
}) => {
|
||||
const { indicatorName } = useIndicatorsFlyoutContext();
|
||||
|
||||
const indicatorType = unwrapValue(indicator, RawIndicatorFieldId.Type);
|
||||
|
||||
const highLevelBlocks = useMemo(
|
||||
|
@ -67,7 +71,10 @@ export const IndicatorsFlyoutOverview: VFC<IndicatorsFlyoutOverviewProps> = ({
|
|||
return unwrappedDescription ? <EuiText>{unwrappedDescription}</EuiText> : null;
|
||||
}, [indicator]);
|
||||
|
||||
const indicatorName = unwrapValue(indicator, RawIndicatorFieldId.Name) || EMPTY_VALUE;
|
||||
const title =
|
||||
indicatorName != null
|
||||
? indicatorName
|
||||
: unwrapValue(indicator, RawIndicatorFieldId.Name) || EMPTY_VALUE;
|
||||
|
||||
if (!indicatorType) {
|
||||
return <IndicatorEmptyPrompt />;
|
||||
|
@ -76,7 +83,7 @@ export const IndicatorsFlyoutOverview: VFC<IndicatorsFlyoutOverviewProps> = ({
|
|||
return (
|
||||
<>
|
||||
<EuiTitle>
|
||||
<h2>{indicatorName}</h2>
|
||||
<h2 data-test-subj={TI_FLYOUT_OVERVIEW_TITLE}>{title}</h2>
|
||||
</EuiTitle>
|
||||
|
||||
{indicatorDescription}
|
||||
|
|
|
@ -21,7 +21,7 @@ export default {
|
|||
component: IndicatorsFlyoutTable,
|
||||
title: 'IndicatorsFlyoutTable',
|
||||
};
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
|
@ -35,7 +35,7 @@ export const Default: Story<void> = () => {
|
|||
return (
|
||||
<KibanaReactContext.Provider>
|
||||
<IndicatorsFiltersContext.Provider value={mockIndicatorsFiltersContext}>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorsFlyoutTable indicator={mockIndicator} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</IndicatorsFiltersContext.Provider>
|
||||
|
@ -45,7 +45,7 @@ export const Default: Story<void> = () => {
|
|||
|
||||
export const EmptyIndicator: Story<void> = () => {
|
||||
return (
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorsFlyoutTable indicator={{ fields: {} } as unknown as Indicator} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
);
|
||||
|
|
|
@ -22,13 +22,13 @@ const mockIndicator: Indicator = generateMockIndicator();
|
|||
|
||||
describe('<IndicatorsFlyoutTable />', () => {
|
||||
it('should render fields and values in table', () => {
|
||||
const kqlBarIntegration = {
|
||||
const context = {
|
||||
kqlBarIntegration: false,
|
||||
};
|
||||
|
||||
const { getByTestId, getByText, getAllByText } = render(
|
||||
<TestProvidersComponent>
|
||||
<IndicatorsFlyoutContext.Provider value={kqlBarIntegration}>
|
||||
<IndicatorsFlyoutContext.Provider value={context}>
|
||||
<IndicatorsFlyoutTable indicator={mockIndicator} />
|
||||
</IndicatorsFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue