Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
178b865
Compress highlights table
NicholasPeretti Aug 1, 2025
d2fc70f
Improve flyout table
NicholasPeretti Aug 5, 2025
c2bce15
Update json view
NicholasPeretti Aug 5, 2025
25e0b66
[CI] Auto-commit changed files from 'node scripts/eslint_all_files --…
kibanamachine Aug 5, 2025
968acdf
Move options from useMemo to module's scope
NicholasPeretti Aug 18, 2025
68849ff
Remove IndicatorValueActions
NicholasPeretti Aug 18, 2025
0e4b7d7
Refactor JsonTab to be reusable
NicholasPeretti Aug 19, 2025
504ed3f
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Aug 19, 2025
57c0bca
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Aug 19, 2025
18fef14
Apply PR feedback
NicholasPeretti Aug 26, 2025
20e3102
Apply PR feedback
NicholasPeretti Aug 28, 2025
7d4816c
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Aug 29, 2025
e0e0a4f
Applh PR feedback
NicholasPeretti Aug 29, 2025
4e673df
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Aug 29, 2025
0cd76a9
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Sep 2, 2025
ff711a5
Fix translations
NicholasPeretti Sep 2, 2025
56cd64f
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Sep 3, 2025
22e7f2f
Fix unit tests
NicholasPeretti Sep 4, 2025
d6f51e4
Fix cypress tests
NicholasPeretti Sep 5, 2025
e819899
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Sep 8, 2025
204cfde
Fix cypress tests
NicholasPeretti Sep 8, 2025
279cb0a
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Sep 16, 2025
2093bba
Fix CI
NicholasPeretti Sep 17, 2025
d51f056
Fix Cypress tests
NicholasPeretti Sep 18, 2025
fcccfc9
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Sep 18, 2025
e114a5d
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Sep 22, 2025
a9d27f4
Merge branch 'main' into 189870-convert-ioc-flyout
NicholasPeretti Sep 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import { render } from '@testing-library/react';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { DocumentDetailsContext } from '../../shared/context';
import { JsonTab } from './json_tab';
import { JSON_TAB_CONTENT_TEST_ID, JSON_TAB_COPY_TO_CLIPBOARD_BUTTON_TEST_ID } from './test_ids';
import {
JSON_TAB_CONTENT_TEST_ID,
JSON_TAB_COPY_TO_CLIPBOARD_BUTTON_TEST_ID,
} from '../../shared/test_ids';

jest.mock('@elastic/eui', () => ({
...jest.requireActual('@elastic/eui'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,88 +5,23 @@
* 2.0.
*/

import React, { memo, useEffect, useRef, useState } from 'react';
import { JsonCodeEditor } from '@kbn/unified-doc-viewer-plugin/public';
import { EuiButtonEmpty, EuiCopy, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { JSON_TAB_CONTENT_TEST_ID, JSON_TAB_COPY_TO_CLIPBOARD_BUTTON_TEST_ID } from './test_ids';
import React, { memo } from 'react';
import { useDocumentDetailsContext } from '../../shared/context';

const FLYOUT_BODY_PADDING = 24;
const COPY_TO_CLIPBOARD_BUTTON_HEIGHT = 24;
const FLYOUT_FOOTER_HEIGHT = 72;
import { JsonTab as SharedJsonTab } from '../../../shared/components/json_tab';
import { PREFIX } from '../../../shared/test_ids';

/**
* Json view displayed in the document details expandable flyout right section
*/
export const JsonTab = memo(() => {
const { searchHit, isRulePreview } = useDocumentDetailsContext();
const jsonValue = JSON.stringify(searchHit, null, 2);

const flexGroupElement = useRef<HTMLDivElement>(null);
const [editorHeight, setEditorHeight] = useState<number>();

useEffect(() => {
const topPosition = flexGroupElement?.current?.getBoundingClientRect().top || 0;
const footerOffset = isRulePreview ? 0 : FLYOUT_FOOTER_HEIGHT;
const height =
window.innerHeight -
topPosition -
COPY_TO_CLIPBOARD_BUTTON_HEIGHT -
FLYOUT_BODY_PADDING -
footerOffset;

if (height === 0) {
return;
}

setEditorHeight(height);
}, [setEditorHeight, isRulePreview]);

return (
<EuiFlexGroup
ref={flexGroupElement}
direction="column"
gutterSize="none"
data-test-subj={JSON_TAB_CONTENT_TEST_ID}
>
<EuiFlexItem>
<EuiFlexGroup justifyContent={'flexEnd'}>
<EuiFlexItem grow={false}>
<EuiCopy textToCopy={jsonValue}>
{(copy) => (
<EuiButtonEmpty
iconType={'copyClipboard'}
size={'xs'}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.right.jsonTab.copyToClipboardButtonAriaLabel',
{
defaultMessage: 'Copy to clipboard',
}
)}
data-test-subj={JSON_TAB_COPY_TO_CLIPBOARD_BUTTON_TEST_ID}
onClick={copy}
onKeyDown={copy}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.jsonTab.copyToClipboardButtonLabel"
defaultMessage="Copy to clipboard"
/>
</EuiButtonEmpty>
)}
</EuiCopy>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<JsonCodeEditor
json={searchHit as unknown as Record<string, unknown>}
height={editorHeight}
hasLineNumbers={true}
/>
</EuiFlexItem>
</EuiFlexGroup>
<SharedJsonTab
value={searchHit as unknown as Record<string, unknown>}
showFooterOffset={isRulePreview}
data-test-subj={PREFIX}
/>
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,3 @@ import { PREFIX } from '../../../shared/test_ids';

export const TABLE_TAB_CONTENT_TEST_ID = `${PREFIX}DocumentTable` as const;
export const TABLE_TAB_SEARCH_INPUT_TEST_ID = `${PREFIX}DocumentTableSearchInput` as const;
export const JSON_TAB_CONTENT_TEST_ID = 'jsonView' as const;
export const JSON_TAB_COPY_TO_CLIPBOARD_BUTTON_TEST_ID = `${PREFIX}JsonTabCopyToClipboard` as const;
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ import { FLYOUT_BODY_TEST_ID } from './test_ids';
import { DocumentDetailsContext } from '../../../document_details/shared/context';
import { mockContextValue } from '../../../document_details/shared/mocks/mock_context';
import userEvent from '@testing-library/user-event';
import {
JSON_TAB_CONTENT_TEST_ID,
TABLE_TAB_CONTENT_TEST_ID,
} from '../../../document_details/right/tabs/test_ids';
import { TABLE_TAB_CONTENT_TEST_ID } from '../../../document_details/right/tabs/test_ids';
import { JSON_TAB_CONTENT_TEST_ID } from '../../../shared/components/json_tab';

describe('AssetDocumentTab', () => {
it('renders', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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, { memo, useEffect, useRef, useState } from 'react';
import { JsonCodeEditor } from '@kbn/unified-doc-viewer-plugin/public';
import { EuiButtonEmpty, EuiCopy, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';

import { PREFIX } from '../test_ids';

export const JSON_TAB_CONTENT_TEST_ID = 'jsonView' as const;
export const JSON_TAB_COPY_TO_CLIPBOARD_BUTTON_TEST_ID = `${PREFIX}JsonTabCopyToClipboard` as const;

// import { useDocumentDetailsContext } from './context';

const FLYOUT_BODY_PADDING = 24;
const COPY_TO_CLIPBOARD_BUTTON_HEIGHT = 24;
const FLYOUT_FOOTER_HEIGHT = 72;

export interface JsonTabProps {
/**
* The data-test-subj to prefix the component one's
*/
['data-test-subj']?: string;
/**
* Use to influence the height of the JsonCodeEditor (in some place the flyout does not have a footer).
*/
showFooterOffset: boolean;
/**
* Json value to render in the JsonCodeEditor
*/
value: Record<string, unknown>;
}

/**
* Json view displayed in the document details expandable flyout right section and in the indicator flyout.
*/
export const JsonTab = memo(
({ value, showFooterOffset, 'data-test-subj': dataTestSubj }: JsonTabProps) => {
const jsonValue = JSON.stringify(value, null, 2);

const flexGroupElement = useRef<HTMLDivElement>(null);
const [editorHeight, setEditorHeight] = useState<number>();

useEffect(() => {
const topPosition = flexGroupElement?.current?.getBoundingClientRect().top || 0;
const footerOffset = showFooterOffset ? 0 : FLYOUT_FOOTER_HEIGHT;
const height =
window.innerHeight -
topPosition -
COPY_TO_CLIPBOARD_BUTTON_HEIGHT -
FLYOUT_BODY_PADDING -
footerOffset;

if (height === 0) {
return;
}

setEditorHeight(height);
}, [setEditorHeight, showFooterOffset]);

return (
<EuiFlexGroup
ref={flexGroupElement}
direction="column"
gutterSize="none"
data-test-subj={`${dataTestSubj}${JSON_TAB_CONTENT_TEST_ID}`}
>
<EuiFlexItem>
<EuiFlexGroup justifyContent={'flexEnd'}>
<EuiFlexItem grow={false}>
<EuiCopy textToCopy={jsonValue}>
{(copy) => (
<EuiButtonEmpty
iconType={'copyClipboard'}
size={'xs'}
aria-label={i18n.translate(
'xpack.securitySolution.flyout.shared.jsonTab.copyToClipboardButtonAriaLabel',
{
defaultMessage: 'Copy to clipboard',
}
)}
data-test-subj={`${dataTestSubj}${JSON_TAB_COPY_TO_CLIPBOARD_BUTTON_TEST_ID}`}
onClick={copy}
onKeyDown={copy}
>
<FormattedMessage
id="xpack.securitySolution.flyout.shared.jsonTab.copyToClipboardButtonLabel"
defaultMessage="Copy to clipboard"
/>
</EuiButtonEmpty>
)}
</EuiCopy>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<JsonCodeEditor json={value} height={editorHeight} hasLineNumbers={true} />
</EuiFlexItem>
</EuiFlexGroup>
);
}
);

JsonTab.displayName = 'JsonTab';
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import { css, euiStyled } from '@kbn/kibana-react-plugin/common';
import type { Indicator } from '../../../../../../common/threat_intelligence/types/indicator';
import { IndicatorFieldValue } from '../common/field_value';
import { IndicatorFieldLabel } from '../common/field_label';
import { IndicatorValueActions } from './indicator_value_actions';
import {
CellActionsMode,
SecurityCellActions,
SecurityCellActionsTrigger,
} from '../../../../../common/components/cell_actions';
import { getIndicatorFieldAndValue } from '../../utils/field_value';

/**
* Show actions wrapper on hover. This is a helper component, limited only to Block
Expand Down Expand Up @@ -56,24 +61,23 @@ export const IndicatorBlock: FC<IndicatorBlockProps> = ({
indicator,
'data-test-subj': dataTestSubj,
}) => {
const { key, value } = getIndicatorFieldAndValue(indicator, field);

return (
<EuiPanel {...panelProps}>
<VisibleOnHover data-test-subj={`${dataTestSubj}Item`}>
<SecurityCellActions
data={{ field: key, value }}
mode={CellActionsMode.HOVER_DOWN}
triggerId={SecurityCellActionsTrigger.DEFAULT}
>
<EuiText>
<IndicatorFieldLabel field={field} />
</EuiText>
<EuiSpacer size="s" />
<EuiText size="s">
<IndicatorFieldValue indicator={indicator} field={field} />
<span className="actionsWrapper">
<IndicatorValueActions
indicator={indicator}
field={field}
data-test-subj={dataTestSubj}
/>
</span>
</EuiText>
</VisibleOnHover>
</SecurityCellActions>
</EuiPanel>
);
};
Loading