[Security Solution][Alert details] remove enableVisualizationsInFlyout advanced setting (#220590)

## Summary

This PR removes the `securitySolution:enableVisualizationsInFlyout`
advanced settings that allowed users to switch between Analyzer and
Session View components displayed in an overlay on top of tables, or
within the flyout expanded panel.
The setting was already defaulted to true, so this PR just removes the
ability to go back to the analyzer and session view components displayed
as table overlay.

This PR is the first of 4 that perform the clean up:
- this first PR that removes the advanced setting
- a follow up PR that will remove the code related to rendering session
view as overlay
- a follow up PR that will remove the code related to rendering analyzer
as overlay
- a final clean up that removes a lot of dead code related to timeline
full screen and such

The work is divided in multiple PRs because when I got everything
working locally it ended up touching nearly 120 files, making the review
difficult...

---------------------------

No more advanced settings
![Screenshot 2025-05-08 at 4 55
24 PM](https://github.com/user-attachments/assets/64f2b80f-afcc-4c76-a6b3-343dbf248a2b)

The alerts table goes to the flyout


https://github.com/user-attachments/assets/b8b934ba-ea9f-4bb2-aaf4-0f73f359eb3a

The table still works from the Cases page


https://github.com/user-attachments/assets/65ffe289-3a3c-4766-aa52-4f68a2f2dd87

And from timeline (the tabs are not showing up either)


https://github.com/user-attachments/assets/0bb17b2f-b6b7-4417-9212-2302b778cc23

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Philippe Oberti 2025-05-23 12:02:50 -05:00 committed by GitHub
parent 5bc61bff26
commit 91106944ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 266 additions and 1020 deletions

View file

@ -1580,7 +1580,7 @@
"\"securitySolution:enableVisualizationsInFlyout\""
],
"path": "src/platform/packages/shared/kbn-management/settings/setting_ids/index.ts",
"deprecated": false,
"deprecated": true,
"trackAdoption": false,
"initialIsOpen": false
},

View file

@ -23,7 +23,6 @@ export const SECURITY_PROJECT_SETTINGS = [
settings.SECURITY_SOLUTION_NEWS_FEED_URL_ID,
settings.SECURITY_SOLUTION_ENABLE_NEWS_FEED_ID,
settings.SECURITY_SOLUTION_DEFAULT_ALERT_TAGS_KEY,
settings.SECURITY_SOLUTION_ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING,
settings.SECURITY_SOLUTION_ENABLE_GRAPH_VISUALIZATION_SETTING,
settings.SECURITY_SOLUTION_ENABLE_ASSET_INVENTORY_SETTING,
];

View file

@ -39396,7 +39396,6 @@
"xpack.securitySolution.flyout.right.response.sectionTitle": "Réponse",
"xpack.securitySolution.flyout.right.rule.rulePreviewTitle": "Afficher les détails de la règle",
"xpack.securitySolution.flyout.right.user.userPreviewTitle": "Aperçu des détails de l'utilisateur",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewInvestigateTooltip": "Investiguer dans la chronologie",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewOpenAnalyzerTooltip": "Ouvrir l'analyseur de graphe",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewTitle": "Aperçu de l'analyseur",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.errorDescription": "Une erreur empêche l'analyse de cette alerte.",
@ -41229,14 +41228,11 @@
"xpack.securitySolution.timeline.searchOrFilter.searchKqlTooltip": "Les événements des fournisseurs de données ci-dessus sont combinés avec les résultats de ce KQL",
"xpack.securitySolution.timeline.sidePanel.maxAnomalyScoreByJobTitle": "Score maximal d'anomalie par tâche",
"xpack.securitySolution.timeline.source": "Source",
"xpack.securitySolution.timeline.tabs.analyserTabTimelineTitle": "Analyseur",
"xpack.securitySolution.timeline.tabs.discoverEsqlInTimeline": "ES|QL",
"xpack.securitySolution.timeline.tabs.eqlTabTimelineTitle": "Corrélation",
"xpack.securitySolution.timeline.tabs.notesTabTimelineTitle": "Notes",
"xpack.securitySolution.timeline.tabs.pinnedTabTimelineTitle": "Épinglé",
"xpack.securitySolution.timeline.tabs.queyTabTimelineTitle": "Recherche",
"xpack.securitySolution.timeline.tabs.securityAssistantTimelineTitle": "Assistant dintelligence artificielle dElastic",
"xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle": "Vue de session",
"xpack.securitySolution.timeline.tcp": "TCP",
"xpack.securitySolution.timeline.toast.insufficientPrivileges.text": "Vous essayez d'ouvrir une chronologie, mais vous ne disposez pas de privilèges suffisants. Veuillez contacter votre administrateur afin d'obtenir les bons privilèges pour la chronologie.",
"xpack.securitySolution.timeline.toast.insufficientPrivileges.title": "Privilèges insuffisants (choronologie)",
@ -41360,12 +41356,9 @@
"xpack.securitySolution.uiSettings.defaultTimeRangeLabel": "Période du filtre de temps",
"xpack.securitySolution.uiSettings.enableCcsReadWarningLabel": "Avertissement lié aux privilèges de la règle CCS",
"xpack.securitySolution.uiSettings.enableCcsWarningDescription": "<p>Active les avertissements de vérification des privilèges dans les règles relatives aux index CCS</p>",
"xpack.securitySolution.uiSettings.enableGraphVisualizationDescription": "<em>[version d'évaluation technique]</em> Activez la fonctionnalité de visualisation de graphe dans la solution de sécurité. <br/>Remarque : Cette fonctionnalité nécessite que le paramètre {visualizationFlyoutFeatureFlag} soit activé. <br/>Veuillez vous assurer que les deux paramètres sont activés afin d'utiliser les visualisations de graphe dans le menu volant.",
"xpack.securitySolution.uiSettings.enableGraphVisualizationLabel": "Activer la visualisation de graphique",
"xpack.securitySolution.uiSettings.enableNewsFeedDescription": "<p>Active le fil d'actualités</p>",
"xpack.securitySolution.uiSettings.enableNewsFeedLabel": "Fil d'actualités",
"xpack.securitySolution.uiSettings.enableVisualizationsInFlyoutDescription": "Activez les visualisations (analyseur et visualiseur de session) dans le menu volant des détails du document.",
"xpack.securitySolution.uiSettings.enableVisualizationsInFlyoutLabel": "Activer les visualisations dans le menu volant",
"xpack.securitySolution.uiSettings.excludeColdAndFrozenTiersInAnalyzer": "Exclure les niveaux froids de l'analyseur",
"xpack.securitySolution.uiSettings.excludeColdAndFrozenTiersInAnalyzerDescription": "<p>Lorsque cette option est activée, les niveaux \"cold\" et \"frozen\" sont ignorés dans les requêtes de l'analyseur</p>",
"xpack.securitySolution.uiSettings.excludedDataTiersForRuleExecutionDescription": "Une fois configurés, les événements des niveaux de données spécifiés ne sont pas recherchés lors des exécutions de règles. <br/>Cela peut contribuer à l'amélioration des performances des règles ou à la réduction du temps d'exécution. <br/>Si vous spécifiez plusieurs niveaux de données, séparez les valeurs par des virgules. Par exemple : data_frozen,data_cold",

View file

@ -39358,7 +39358,6 @@
"xpack.securitySolution.flyout.right.response.sectionTitle": "応答",
"xpack.securitySolution.flyout.right.rule.rulePreviewTitle": "ルール詳細をプレビュー",
"xpack.securitySolution.flyout.right.user.userPreviewTitle": "ユーザー詳細をプレビュー",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewInvestigateTooltip": "タイムラインで調査",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewOpenAnalyzerTooltip": "アナライザーグラフを開く",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewTitle": "アナライザープレビュー",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.errorDescription": "エラーが発生したため、このアラートを分析できません。",
@ -41192,14 +41191,11 @@
"xpack.securitySolution.timeline.searchOrFilter.searchKqlTooltip": "上のデータプロバイダーからのイベントは、この KQL からの結果と組み合わされます。",
"xpack.securitySolution.timeline.sidePanel.maxAnomalyScoreByJobTitle": "ジョブ別の最高異常スコア",
"xpack.securitySolution.timeline.source": "送信元",
"xpack.securitySolution.timeline.tabs.analyserTabTimelineTitle": "アナライザー",
"xpack.securitySolution.timeline.tabs.discoverEsqlInTimeline": "ES|QL",
"xpack.securitySolution.timeline.tabs.eqlTabTimelineTitle": "相関関係",
"xpack.securitySolution.timeline.tabs.notesTabTimelineTitle": "メモ",
"xpack.securitySolution.timeline.tabs.pinnedTabTimelineTitle": "ピン付け済み",
"xpack.securitySolution.timeline.tabs.queyTabTimelineTitle": "クエリー",
"xpack.securitySolution.timeline.tabs.securityAssistantTimelineTitle": "Elastic AI Assistant",
"xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle": "セッションビュー",
"xpack.securitySolution.timeline.tcp": "TCP",
"xpack.securitySolution.timeline.toast.insufficientPrivileges.text": "タイムラインを開こうとしていますが、権限が不十分です。タイムラインに適切な権限を設定するには、管理者に依頼してください。",
"xpack.securitySolution.timeline.toast.insufficientPrivileges.title": "権限が不十分です(タイムライン)",
@ -41323,12 +41319,9 @@
"xpack.securitySolution.uiSettings.defaultTimeRangeLabel": "時間フィルターの期間",
"xpack.securitySolution.uiSettings.enableCcsReadWarningLabel": "CCSルール権限警告",
"xpack.securitySolution.uiSettings.enableCcsWarningDescription": "<p>CCSインデックスのルールで権限チェック警告を有効にします</p>",
"xpack.securitySolution.uiSettings.enableGraphVisualizationDescription": "<em>[テクニカルプレビュー]</em> Securityソリューション内でグラフ視覚化機能を有効にします。<br/>注:この機能を使用するには、{visualizationFlyoutFeatureFlag}設定を有効にする必要があります。<br/>フライアウトでグラフ視覚化を実行するには、両方の設定が有効になっていることを確認してください。",
"xpack.securitySolution.uiSettings.enableGraphVisualizationLabel": "グラフビジュアライゼーションを有効化",
"xpack.securitySolution.uiSettings.enableNewsFeedDescription": "<p>ニュースフィードを有効にします</p>",
"xpack.securitySolution.uiSettings.enableNewsFeedLabel": "ニュースフィード",
"xpack.securitySolution.uiSettings.enableVisualizationsInFlyoutDescription": "ドキュメント詳細フライアウトで視覚化(アナライザーとセッションビューアー)を有効にします。",
"xpack.securitySolution.uiSettings.enableVisualizationsInFlyoutLabel": "フライアウトでビジュアライゼーションを有効化",
"xpack.securitySolution.uiSettings.excludeColdAndFrozenTiersInAnalyzer": "アナライザーでコールドティアとフローズンティアを除外",
"xpack.securitySolution.uiSettings.excludeColdAndFrozenTiersInAnalyzerDescription": "<p>有効にすると、アナライザークエリーでコールドティアとフローズンティアがスキップされます</p>",
"xpack.securitySolution.uiSettings.excludedDataTiersForRuleExecutionDescription": "構成されていると、指定したデータティアのイベントがルール実行中に検索されません。<br/>このため、ルールパフォーマンスが改善されたり、実行時間が短縮されたりする場合があります。<br/>複数のデータティアを指定する場合は、値をカンマで区切ってください。例data_frozen,data_cold",

View file

@ -39442,7 +39442,6 @@
"xpack.securitySolution.flyout.right.response.sectionTitle": "响应",
"xpack.securitySolution.flyout.right.rule.rulePreviewTitle": "预览规则详情",
"xpack.securitySolution.flyout.right.user.userPreviewTitle": "预览用户详情",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewInvestigateTooltip": "在时间线中调查",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewOpenAnalyzerTooltip": "打开分析器图表",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewTitle": "分析器预览",
"xpack.securitySolution.flyout.right.visualizations.analyzerPreview.errorDescription": "出现错误,无法分析此告警。",
@ -41276,14 +41275,11 @@
"xpack.securitySolution.timeline.searchOrFilter.searchKqlTooltip": "来自上述数据提供程序的事件与此 KQL 的结果进行组合",
"xpack.securitySolution.timeline.sidePanel.maxAnomalyScoreByJobTitle": "最大异常分数(按作业)",
"xpack.securitySolution.timeline.source": "源",
"xpack.securitySolution.timeline.tabs.analyserTabTimelineTitle": "分析器",
"xpack.securitySolution.timeline.tabs.discoverEsqlInTimeline": "ES|QL",
"xpack.securitySolution.timeline.tabs.eqlTabTimelineTitle": "相关性",
"xpack.securitySolution.timeline.tabs.notesTabTimelineTitle": "备注",
"xpack.securitySolution.timeline.tabs.pinnedTabTimelineTitle": "已固定",
"xpack.securitySolution.timeline.tabs.queyTabTimelineTitle": "查询",
"xpack.securitySolution.timeline.tabs.securityAssistantTimelineTitle": "Elastic AI 助手",
"xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle": "会话视图",
"xpack.securitySolution.timeline.tcp": "TCP",
"xpack.securitySolution.timeline.toast.insufficientPrivileges.text": "您正尝试打开时间线,但您的权限不足。请联系管理员以便为时间线设置正确的权限。",
"xpack.securitySolution.timeline.toast.insufficientPrivileges.title": "权限不足(时间线)",
@ -41407,12 +41403,9 @@
"xpack.securitySolution.uiSettings.defaultTimeRangeLabel": "时间筛选时段",
"xpack.securitySolution.uiSettings.enableCcsReadWarningLabel": "CCS 规则权限警告",
"xpack.securitySolution.uiSettings.enableCcsWarningDescription": "<p>在规则中为 CCS 索引启用权限检查警告</p>",
"xpack.securitySolution.uiSettings.enableGraphVisualizationDescription": "<em>[技术预览]</em> 在安全解决方案中启用 Graph 可视化功能。<br/>注意:此功能需要启用 {visualizationFlyoutFeatureFlag} 设置。<br/>请确保启用这两项设置以在浮出控件中使用 Graph 可视化。",
"xpack.securitySolution.uiSettings.enableGraphVisualizationLabel": "启用 Graph 可视化",
"xpack.securitySolution.uiSettings.enableNewsFeedDescription": "<p>启用新闻源</p>",
"xpack.securitySolution.uiSettings.enableNewsFeedLabel": "新闻源",
"xpack.securitySolution.uiSettings.enableVisualizationsInFlyoutDescription": "在文档详情浮出控件中启用可视化(分析器和会话查看器)。",
"xpack.securitySolution.uiSettings.enableVisualizationsInFlyoutLabel": "在浮出控件中启用可视化",
"xpack.securitySolution.uiSettings.excludeColdAndFrozenTiersInAnalyzer": "在分析器中排除冷层和冻结层",
"xpack.securitySolution.uiSettings.excludeColdAndFrozenTiersInAnalyzerDescription": "<p>启用后,将在分析器查询中跳过冷层和冻结层</p>",
"xpack.securitySolution.uiSettings.excludedDataTiersForRuleExecutionDescription": "配置之后,将不会在执行规则期间搜索指定数据层中的事件。<br/>这可能有助于提高规则性能或缩短执行时间。<br/>如果指定了多个数据层请用逗号分隔值。例如data_frozen,data_cold",

View file

@ -19,7 +19,6 @@ export { SecurityPageName } from '@kbn/security-solution-navigation';
*/
export const APP_ID = 'securitySolution' as const;
export const APP_UI_ID = 'securitySolutionUI' as const;
export const ASSET_INVENTORY_FEATURE_ID = 'securitySolutionAssetInventory' as const;
export const ASSISTANT_FEATURE_ID = 'securitySolutionAssistant' as const;
export const ATTACK_DISCOVERY_FEATURE_ID = 'securitySolutionAttackDiscovery' as const;
export const CASES_FEATURE_ID = 'securitySolutionCasesV3' as const;
@ -28,7 +27,6 @@ export const NOTES_FEATURE_ID = 'securitySolutionNotes' as const;
export const SERVER_APP_ID = 'siem' as const;
export const SECURITY_FEATURE_ID = 'siemV2' as const;
export const APP_NAME = 'Security' as const;
export const APP_ICON = 'securityAnalyticsApp' as const;
export const APP_ICON_SOLUTION = 'logoSecurity' as const;
export const APP_PATH = `/app/security` as const;
export const APP_INTEGRATIONS_PATH = `/app/integrations` as const;
@ -112,9 +110,6 @@ export const NETWORK_PATH = '/network' as const;
export const MANAGEMENT_PATH = '/administration' as const;
export const COVERAGE_OVERVIEW_PATH = '/rules_coverage_overview' as const;
export const THREAT_INTELLIGENCE_PATH = '/threat_intelligence' as const;
export const INVESTIGATIONS_PATH = '/investigations' as const;
export const MACHINE_LEARNING_PATH = '/ml' as const;
export const ASSETS_PATH = '/assets' as const;
export const ENDPOINTS_PATH = `${MANAGEMENT_PATH}/endpoints` as const;
export const POLICIES_PATH = `${MANAGEMENT_PATH}/policy` as const;
export const TRUSTED_APPS_PATH = `${MANAGEMENT_PATH}/trusted_apps` as const;
@ -216,10 +211,6 @@ export const EXTENDED_RULE_EXECUTION_LOGGING_MIN_LEVEL_SETTING =
export const EXCLUDED_DATA_TIERS_FOR_RULE_EXECUTION =
'securitySolution:excludedDataTiersForRuleExecution' as const;
/** This Kibana Advanced Setting allows users to enable/disable the Visualizations in Flyout feature */
export const ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING =
'securitySolution:enableVisualizationsInFlyout' as const;
/** This Kibana Advanced Setting allows users to enable/disable the Graph Visualizations for alerts and events */
export const ENABLE_GRAPH_VISUALIZATION_SETTING =
'securitySolution:enableGraphVisualization' as const;
@ -292,7 +283,6 @@ export const TIMELINE_COPY_URL = `${TIMELINE_URL}/_copy` as const;
export const NOTE_URL = '/api/note' as const;
export const PINNED_EVENT_URL = '/api/pinned_event' as const;
export const SOURCERER_API_URL = '/internal/security_solution/sourcerer' as const;
/**
* This limit exists to maintain some kind of a safety net for how many events we are fetching in total,
@ -360,18 +350,6 @@ export const NOTIFICATION_DEFAULT_FREQUENCY = {
summary: true,
};
export const showAllOthersBucket: string[] = [
'destination.ip',
'event.action',
'event.category',
'event.dataset',
'event.module',
'signal.rule.threat.tactic.name',
'source.ip',
'destination.ip',
'user.name',
];
export const TRANSFORM_STATES = {
ABORTING: 'aborting',
FAILED: 'failed',

View file

@ -46,15 +46,6 @@ jest.mock(
})
);
const mockUseUiSetting = jest.fn().mockReturnValue([true]);
jest.mock('@kbn/kibana-react-plugin/public', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public');
return {
...original,
useUiSetting$: () => mockUseUiSetting(),
};
});
jest.mock('./add_note_icon_item', () => {
return {
AddEventNoteAction: jest.fn(() => <div data-test-subj="add-note-mock-action" />),
@ -76,9 +67,6 @@ jest.mock('../../lib/kibana', () => {
},
},
cases: mockCasesContract(),
uiSettings: {
get: jest.fn(),
},
savedObjects: {
client: {},
},
@ -390,24 +378,6 @@ describe('Actions', () => {
expect(wrapper.find('[data-test-subj="view-in-analyzer"]').exists()).toBe(false);
});
test('it does not render the analyze event button on the cases alerts table with advanced settings disabled', () => {
const ecsData = {
...mockTimelineData[0].ecs,
event: { kind: ['alert'] },
agent: { type: ['endpoint'] },
process: { entity_id: ['1'] },
};
mockUseUiSetting.mockReturnValue([false]);
const wrapper = mount(
<TestProviders>
<Actions {...defaultProps} ecsData={ecsData} timelineId={TableId.alertsOnCasePage} />
</TestProviders>
);
expect(wrapper.find('[data-test-subj="view-in-analyzer"]').exists()).toBe(false);
});
test('it does render the analyze event button on the cases alerts table with advanced settings enabled', () => {
const ecsData = {
...mockTimelineData[0].ecs,
@ -415,7 +385,6 @@ describe('Actions', () => {
agent: { type: ['endpoint'] },
process: { entity_id: ['1'] },
};
mockUseUiSetting.mockReturnValue([true]);
const wrapper = mount(
<TestProviders>
@ -433,7 +402,6 @@ describe('Actions', () => {
agent: { type: ['endpoint'] },
process: { entity_id: ['1'] },
};
mockUseUiSetting.mockReturnValue([false]);
const wrapper = mount(
<TestProviders>
@ -462,28 +430,6 @@ describe('Actions', () => {
expect(wrapper.find('[data-test-subj="session-view-button"]').exists()).toEqual(false);
});
test('it should show session view button on action tabs when user access the session viewer via K8S dashboard', () => {
const ecsData = {
...mockTimelineData[0].ecs,
event: { kind: ['alert'] },
agent: { type: ['endpoint'] },
process: { entry_leader: { entity_id: ['test_id'], start: ['2022-05-08T13:44:00.13Z'] } },
_index: '.ds-logs-endpoint.events.process-default',
};
const wrapper = mount(
<TestProviders>
<Actions
{...defaultProps}
ecsData={ecsData}
timelineId={TableId.kubernetesPageSessions}
/>
</TestProviders>
);
expect(wrapper.find('[data-test-subj="session-view-button"]').exists()).toEqual(true);
});
test('it should show session view button on action tabs for enterprise users', () => {
const licenseServiceMock = licenseService as jest.Mocked<typeof licenseService>;
@ -506,25 +452,6 @@ describe('Actions', () => {
expect(wrapper.find('[data-test-subj="session-view-button"]').exists()).toEqual(true);
});
test('it does not render the session view button on the cases alerts table with advanced settings disabled', () => {
const ecsData = {
...mockTimelineData[0].ecs,
event: { kind: ['alert'] },
agent: { type: ['endpoint'] },
process: { entry_leader: { entity_id: ['test_id'], start: ['2022-05-08T13:44:00.13Z'] } },
_index: '.ds-logs-endpoint.events.process-default',
};
mockUseUiSetting.mockReturnValue([false]);
const wrapper = mount(
<TestProviders>
<Actions {...defaultProps} ecsData={ecsData} timelineId={TableId.alertsOnCasePage} />
</TestProviders>
);
expect(wrapper.find('[data-test-subj="session-view-button"]').exists()).toBe(false);
});
test('it does render the session view button on the cases alerts table with advanced settings enabled', () => {
const ecsData = {
...mockTimelineData[0].ecs,
@ -533,7 +460,6 @@ describe('Actions', () => {
process: { entry_leader: { entity_id: ['test_id'], start: ['2022-05-08T13:44:00.13Z'] } },
_index: '.ds-logs-endpoint.events.process-default',
};
mockUseUiSetting.mockReturnValue([true]);
const wrapper = mount(
<TestProviders>

View file

@ -9,12 +9,9 @@ import React, { useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import styled from 'styled-components';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { TimelineTabs, TableId } from '@kbn/securitysolution-data-table';
import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../common/constants';
import {
makeSelectNotesByDocumentId,
makeSelectDocumentNotesBySavedObjectId,
makeSelectNotesByDocumentId,
} from '../../../notes/store/notes.slice';
import type { State } from '../../store';
import { selectTimelineById } from '../../../timelines/store/selectors';
@ -23,7 +20,7 @@ import {
getEventType,
getPinOnClick,
} from '../../../timelines/components/timeline/body/helpers';
import { getScopedActions, isTimelineScope } from '../../../helpers';
import { isTimelineScope } from '../../../helpers';
import { useIsInvestigateInResolverActionEnabled } from '../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver';
import { timelineActions } from '../../../timelines/store';
import type { ActionProps, OnPinEvent } from '../../../../common/types';
@ -34,9 +31,7 @@ import { useShallowEqualSelector } from '../../hooks/use_selector';
import { timelineDefaults } from '../../../timelines/store/defaults';
import { useStartTransaction } from '../../lib/apm/use_start_transaction';
import { useLicense } from '../../hooks/use_license';
import { useGlobalFullScreen, useTimelineFullScreen } from '../../containers/use_full_screen';
import { ALERTS_ACTIONS } from '../../lib/apm/user_actions';
import { setActiveTabTimeline } from '../../../timelines/store/actions';
import { EventsTdContent } from '../../../timelines/components/timeline/styles';
import { AlertContextMenu } from '../../../detections/components/alerts_table/timeline_actions/alert_context_menu';
import { InvestigateInTimelineAction } from '../../../detections/components/alerts_table/timeline_actions/investigate_in_timeline_action';
@ -113,10 +108,6 @@ const ActionsComponent: React.FC<ActionProps> = ({
);
}, [ecsData, eventType]);
const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>(
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING
);
const { navigateToAnalyzer } = useNavigateToAnalyzer({
isFlyoutOpen: false,
eventId,
@ -131,41 +122,10 @@ const ActionsComponent: React.FC<ActionProps> = ({
scopeId: timelineId,
});
const { setGlobalFullScreen } = useGlobalFullScreen();
const { setTimelineFullScreen } = useTimelineFullScreen();
const handleClick = useCallback(() => {
startTransaction({ name: ALERTS_ACTIONS.OPEN_ANALYZER });
if (visualizationInFlyoutEnabled) {
navigateToAnalyzer();
} else {
const scopedActions = getScopedActions(timelineId);
const dataGridIsFullScreen = document.querySelector('.euiDataGrid--fullScreen');
if (scopedActions) {
dispatch(scopedActions.updateGraphEventId({ id: timelineId, graphEventId: ecsData._id }));
}
if (timelineId === TimelineId.active) {
if (dataGridIsFullScreen) {
setTimelineFullScreen(true);
}
dispatch(setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.graph }));
} else {
if (dataGridIsFullScreen) {
setGlobalFullScreen(true);
}
}
}
}, [
startTransaction,
timelineId,
dispatch,
ecsData._id,
setTimelineFullScreen,
setGlobalFullScreen,
visualizationInFlyoutEnabled,
navigateToAnalyzer,
]);
navigateToAnalyzer();
}, [startTransaction, navigateToAnalyzer]);
const sessionViewConfig = useMemo(() => {
const { process, _id, _index, timestamp, kibana } = ecsData;
@ -193,46 +153,9 @@ const ActionsComponent: React.FC<ActionProps> = ({
}, [ecsData, eventType]);
const openSessionView = useCallback(() => {
const dataGridIsFullScreen = document.querySelector('.euiDataGrid--fullScreen');
startTransaction({ name: ALERTS_ACTIONS.OPEN_SESSION_VIEW });
if (
visualizationInFlyoutEnabled &&
sessionViewConfig !== null &&
timelineId !== TableId.kubernetesPageSessions
) {
navigateToSessionView();
} else {
const scopedActions = getScopedActions(timelineId);
if (timelineId === TimelineId.active) {
if (dataGridIsFullScreen) {
setTimelineFullScreen(true);
}
if (sessionViewConfig !== null) {
dispatch(setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.session }));
}
} else {
if (dataGridIsFullScreen) {
setGlobalFullScreen(true);
}
}
if (sessionViewConfig !== null) {
if (scopedActions) {
dispatch(scopedActions.updateSessionViewConfig({ id: timelineId, sessionViewConfig }));
}
}
}
}, [
startTransaction,
timelineId,
sessionViewConfig,
setTimelineFullScreen,
dispatch,
setGlobalFullScreen,
visualizationInFlyoutEnabled,
navigateToSessionView,
]);
navigateToSessionView();
}, [navigateToSessionView, startTransaction]);
const { activeStep, isTourShown, incrementStep } = useTourContext();
@ -293,27 +216,17 @@ const ActionsComponent: React.FC<ActionProps> = ({
// we hide the analyzer icon if the data is not available for the resolver
// or if we are on the cases alerts table and the the visualization in flyout advanced setting is disabled
const ecsHasDataForAnalyzer = useIsInvestigateInResolverActionEnabled(ecsData);
const showAnalyzerIcon = useMemo(() => {
return (
ecsHasDataForAnalyzer &&
(timelineId !== TableId.alertsOnCasePage ||
(timelineId === TableId.alertsOnCasePage && visualizationInFlyoutEnabled))
);
}, [ecsHasDataForAnalyzer, timelineId, visualizationInFlyoutEnabled]);
const showAnalyzerIcon = useIsInvestigateInResolverActionEnabled(ecsData);
// we hide the session view icon if the session view is not available
// or if we are on the cases alerts table and the the visualization in flyout advanced setting is disabled
// or if the user is not on an enterprise license or on the kubernetes page
const isEnterprisePlus = useLicense().isEnterprise();
const showSessionViewIcon = useMemo(() => {
return (
sessionViewConfig !== null &&
(isEnterprisePlus || timelineId === TableId.kubernetesPageSessions) &&
(timelineId !== TableId.alertsOnCasePage ||
(timelineId === TableId.alertsOnCasePage && visualizationInFlyoutEnabled))
);
}, [sessionViewConfig, isEnterprisePlus, timelineId, visualizationInFlyoutEnabled]);
const showSessionViewIcon = useMemo(
() => sessionViewConfig !== null && isEnterprisePlus,
[isEnterprisePlus, sessionViewConfig]
);
return (
<ActionsContainer data-test-subj="actions-container">

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { useEffect, useState, useCallback, useMemo, memo, type FC } from 'react';
import React, { type FC, memo, useCallback, useEffect, useMemo, useState } from 'react';
import type { EuiDataGridRowHeightsOptions, EuiDataGridStyle } from '@elastic/eui';
import { EuiFlexGroup } from '@elastic/eui';
import type { Filter } from '@kbn/es-query';
@ -25,19 +25,14 @@ import { useAlertsContext } from './alerts_context';
import { useBulkActionsByTableType } from '../../hooks/trigger_actions_alert_table/use_bulk_actions';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import type {
SecurityAlertsTableContext,
GetSecurityAlertsTableProp,
SecurityAlertsTableContext,
SecurityAlertsTableProps,
} from './types';
import { ActionsCell } from './actions_cell';
import { useGlobalTime } from '../../../common/containers/use_global_time';
import { useLicense } from '../../../common/hooks/use_license';
import {
APP_ID,
CASES_FEATURE_ID,
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING,
VIEW_SELECTION,
} from '../../../../common/constants';
import { APP_ID, CASES_FEATURE_ID, VIEW_SELECTION } from '../../../../common/constants';
import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
import { eventsDefaultModel } from '../../../common/components/events_viewer/default_model';
@ -54,9 +49,9 @@ import { StatefulEventContext } from '../../../common/components/events_viewer/s
import { useSourcererDataView } from '../../../sourcerer/containers';
import type { RunTimeMappings } from '../../../sourcerer/store/model';
import { SourcererScopeName } from '../../../sourcerer/store/model';
import { useKibana, useUiSetting$ } from '../../../common/lib/kibana';
import { useKibana } from '../../../common/lib/kibana';
import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
import { getColumns, CellValue } from '../../configurations/security_solution_detections';
import { CellValue, getColumns } from '../../configurations/security_solution_detections';
import { buildTimeRangeFilter } from './helpers';
import { useUserPrivileges } from '../../../common/components/user_privileges';
import * as i18n from './translations';
@ -169,9 +164,6 @@ const DetectionEngineAlertsTableComponent: FC<Omit<DetectionEngineAlertTableProp
settings,
cases,
} = useKibana().services;
const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>(
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING
);
const { alertsTableRef } = useAlertsContext();
const { from, to, setQuery } = useGlobalTime();
@ -350,15 +342,6 @@ const DetectionEngineAlertsTableComponent: FC<Omit<DetectionEngineAlertTableProp
if (!canReadNotes || securitySolutionNotesDisabled) {
ACTION_BUTTON_COUNT--;
}
// we do not show the analyzer graph and session view icons on the cases alerts tab alerts table
// if the visualization in flyout advanced settings is disabled because these aren't supported inside the table
if (tableType === TableId.alertsOnCasePage) {
if (!isEnterprisePlus && !visualizationInFlyoutEnabled) {
ACTION_BUTTON_COUNT -= 1;
} else if (isEnterprisePlus && !visualizationInFlyoutEnabled) {
ACTION_BUTTON_COUNT -= 2;
}
}
const leadingControlColumn = useMemo(
() => getDefaultControlColumn(ACTION_BUTTON_COUNT)[0],

View file

@ -9,10 +9,8 @@ import type { FC } from 'react';
import React, { memo, useMemo } from 'react';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { useUserPrivileges } from '../../../common/components/user_privileges';
import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../common/constants';
import { DocumentDetailsLeftPanelKey } from '../shared/constants/panel_keys';
import { useKibana } from '../../../common/lib/kibana';
import { PanelHeader } from './header';
@ -44,10 +42,6 @@ export const LeftPanel: FC<Partial<DocumentDetailsProps>> = memo(({ path }) => {
notesPrivileges: { read: canSeeNotes },
} = useUserPrivileges();
const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>(
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING
);
const tabsDisplayed = useMemo(() => {
const tabList =
eventKind === EventKind.signal
@ -56,17 +50,11 @@ export const LeftPanel: FC<Partial<DocumentDetailsProps>> = memo(({ path }) => {
if (canSeeNotes && !securitySolutionNotesDisabled && !isPreview) {
tabList.push(tabs.notesTab);
}
if (visualizationInFlyoutEnabled && !isPreview) {
if (!isPreview) {
return [tabs.visualizeTab, ...tabList];
}
return tabList;
}, [
eventKind,
isPreview,
canSeeNotes,
securitySolutionNotesDisabled,
visualizationInFlyoutEnabled,
]);
}, [eventKind, isPreview, canSeeNotes, securitySolutionNotesDisabled]);
const selectedTabId = useMemo(() => {
const defaultTab = tabsDisplayed[0].id;

View file

@ -39,14 +39,6 @@ jest.mock('../../shared/hooks/use_navigate_to_analyzer', () => {
return { useNavigateToAnalyzer: () => ({ navigateToAnalyzer: mockNavigateToAnalyzer }) };
});
jest.mock('@kbn/kibana-react-plugin/public', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public');
return {
...original,
useUiSetting$: () => mockUseUiSetting(),
};
});
jest.mock('react-router-dom', () => {
const actual = jest.requireActual('react-router-dom');
return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) };
@ -60,15 +52,6 @@ jest.mock('react-redux', () => {
};
});
const mockUseUiSetting = jest.fn().mockReturnValue([false]);
jest.mock('@kbn/kibana-react-plugin/public', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public');
return {
...original,
useUiSetting$: () => mockUseUiSetting(),
};
});
const NO_ANALYZER_MESSAGE =
'You can only visualize events triggered by hosts configured with the Elastic Defend integration or any sysmon data from winlogbeat. Refer to Visual event analyzer(external, opens in a new tab or window) for more information.';
@ -138,132 +121,61 @@ describe('AnalyzerPreviewContainer', () => {
).toHaveTextContent(NO_ANALYZER_MESSAGE);
});
describe('when visualizationInFlyoutEnabled is disabled', () => {
it('should navigate to analyzer in timeline when clicking on title', () => {
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
const { getByTestId } = renderAnalyzerPreview();
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({});
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click();
expect(investigateInTimelineAlertClick).toHaveBeenCalled();
it('should open left flyout visualization tab when clicking on title', () => {
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
it('should not navigate to analyzer when in preview and clicking on title', () => {
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
const { getByTestId } = renderAnalyzerPreview();
const { queryByTestId } = renderAnalyzerPreview({ ...mockContextValue, isPreview: true });
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({});
expect(investigateInTimelineAlertClick).not.toHaveBeenCalled();
});
it('should not navigate to analyzer when in preview mode', () => {
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
const { queryByTestId } = renderAnalyzerPreview({
...mockContextValue,
isPreviewMode: true,
});
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({});
expect(investigateInTimelineAlertClick).not.toHaveBeenCalled();
});
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click();
expect(mockNavigateToAnalyzer).toHaveBeenCalled();
});
describe('when visualizationInFlyoutEnabled is enabled', () => {
it('should open left flyout visualization tab when clicking on title', () => {
mockUseUiSetting.mockReturnValue([true]);
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
const { getByTestId } = renderAnalyzerPreview();
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click();
expect(mockNavigateToAnalyzer).toHaveBeenCalled();
it('should disable link when in rule preview', () => {
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
it('should disable link when in rule preview', () => {
mockUseUiSetting.mockReturnValue([true]);
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
const { queryByTestId } = renderAnalyzerPreview({ ...mockContextValue, isPreview: true });
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
const { queryByTestId } = renderAnalyzerPreview({ ...mockContextValue, isPreview: true });
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
it('should disable link when in preview mode', () => {
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
it('should disable link when in preview mode', () => {
mockUseUiSetting.mockReturnValue([true]);
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
const { queryByTestId } = renderAnalyzerPreview({
...mockContextValue,
isPreviewMode: true,
});
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
const { queryByTestId } = renderAnalyzerPreview({
...mockContextValue,
isPreviewMode: true,
});
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
});
@ -271,82 +183,37 @@ describe('AnalyzerPreviewContainer', () => {
beforeEach(() => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
});
describe('when visualizationInFlyoutEnabled is enabled', () => {
beforeEach(() => {
mockUseUiSetting.mockReturnValue([true]);
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
beforeEach(() => {
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
it('should open left flyout visualization tab when clicking on title', () => {
const { getByTestId } = renderAnalyzerPreview();
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click();
expect(mockNavigateToAnalyzer).toHaveBeenCalled();
});
it('should disable link when in rule preview', () => {
const { queryByTestId } = renderAnalyzerPreview({ ...mockContextValue, isPreview: true });
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
it('should render link when in preview mode', () => {
const { getByTestId } = renderAnalyzerPreview({ ...mockContextValue, isPreviewMode: true });
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click();
expect(mockNavigateToAnalyzer).toHaveBeenCalled();
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
});
it('should open left flyout visualization tab when clicking on title', () => {
const { getByTestId } = renderAnalyzerPreview();
describe('when visualizationInFlyoutEnabled is disabled', () => {
beforeEach(() => {
mockUseUiSetting.mockReturnValue([false]);
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true);
(useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
});
it('should navigate to analyzer in timeline when clicking on title', () => {
const { getByTestId } = renderAnalyzerPreview();
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({});
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click();
expect(mockNavigateToAnalyzer).toHaveBeenCalled();
});
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click();
expect(investigateInTimelineAlertClick).toHaveBeenCalled();
});
it('should disable link when in rule preview', () => {
const { queryByTestId } = renderAnalyzerPreview({ ...mockContextValue, isPreview: true });
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
it('should not navigate to analyzer when in preview and clicking on title', () => {
const { queryByTestId } = renderAnalyzerPreview({ ...mockContextValue, isPreview: true });
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({});
expect(investigateInTimelineAlertClick).not.toHaveBeenCalled();
});
it('should render link when in preview mode', () => {
const { getByTestId } = renderAnalyzerPreview({ ...mockContextValue, isPreviewMode: true });
it('should open analyzer in timelinewhen in preview mode', () => {
const { getByTestId } = renderAnalyzerPreview({
...mockContextValue,
isPreviewMode: true,
});
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({});
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click();
expect(investigateInTimelineAlertClick).toHaveBeenCalled();
});
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click();
expect(mockNavigateToAnalyzer).toHaveBeenCalled();
});
});
});

View file

@ -5,18 +5,9 @@
* 2.0.
*/
import React, { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { TimelineTabs } from '@kbn/securitysolution-data-table';
import React, { useMemo } from 'react';
import { EuiLink, EuiMark } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../../common/constants';
import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction';
import { useInvestigateInTimeline } from '../../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline';
import { ALERTS_ACTIONS } from '../../../../common/lib/apm/user_actions';
import { getScopedActions } from '../../../../helpers';
import { setActiveTabTimeline } from '../../../../timelines/store/actions';
import { useDocumentDetailsContext } from '../../shared/context';
import { useIsInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver';
import { AnalyzerPreview } from './analyzer_preview';
@ -25,8 +16,6 @@ import { useNavigateToAnalyzer } from '../../shared/hooks/use_navigate_to_analyz
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
const timelineId = 'timeline-1';
/**
* Analyzer preview under Overview, Visualizations. It shows a tree representation of analyzer.
*/
@ -34,37 +23,12 @@ export const AnalyzerPreviewContainer: React.FC = () => {
const { dataAsNestedObject, isPreview, eventId, indexName, scopeId, isPreviewMode } =
useDocumentDetailsContext();
const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>(
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING
);
const isNewNavigationEnabled = !useIsExperimentalFeatureEnabled(
'newExpandableFlyoutNavigationDisabled'
);
// decide whether to show the analyzer preview or not
const isEnabled = useIsInvestigateInResolverActionEnabled(dataAsNestedObject);
const dispatch = useDispatch();
const { startTransaction } = useStartTransaction();
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({
ecsRowData: dataAsNestedObject,
});
// open timeline to the analyzer tab because the expandable flyout left panel Visualize => Analyzer tab is not ready
const goToAnalyzerTab = useCallback(async () => {
// open timeline
await investigateInTimelineAlertClick();
// open analyzer tab
startTransaction({ name: ALERTS_ACTIONS.OPEN_ANALYZER });
const scopedActions = getScopedActions(timelineId);
if (scopedActions && dataAsNestedObject) {
dispatch(
scopedActions.updateGraphEventId({ id: timelineId, graphEventId: dataAsNestedObject._id })
);
}
dispatch(setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.graph }));
}, [dataAsNestedObject, dispatch, investigateInTimelineAlertClick, startTransaction]);
const { navigateToAnalyzer } = useNavigateToAnalyzer({
eventId,
indexName,
@ -73,10 +37,7 @@ export const AnalyzerPreviewContainer: React.FC = () => {
isPreviewMode,
});
const iconType = useMemo(() => {
const icon = visualizationInFlyoutEnabled ? 'arrowStart' : 'timeline';
return !isPreviewMode ? icon : undefined;
}, [visualizationInFlyoutEnabled, isPreviewMode]);
const iconType = useMemo(() => (!isPreviewMode ? 'arrowStart' : undefined), [isPreviewMode]);
const isNavigationEnabled = useMemo(() => {
// if the analyzer is not enabled or in rule preview mode, the navigation is not enabled
@ -103,17 +64,12 @@ export const AnalyzerPreviewContainer: React.FC = () => {
iconType,
...(isNavigationEnabled && {
link: {
callback: visualizationInFlyoutEnabled ? navigateToAnalyzer : goToAnalyzerTab,
tooltip: visualizationInFlyoutEnabled ? (
callback: navigateToAnalyzer,
tooltip: (
<FormattedMessage
id="xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewOpenAnalyzerTooltip"
defaultMessage="Open analyzer graph"
/>
) : (
<FormattedMessage
id="xpack.securitySolution.flyout.right.visualizations.analyzerPreview.analyzerPreviewInvestigateTooltip"
defaultMessage="Investigate in timeline"
/>
),
},
}),

View file

@ -9,8 +9,8 @@ import React from 'react';
import { render } from '@testing-library/react';
import { useFetchGraphData } from '@kbn/cloud-security-posture-graph/src/hooks';
import {
uiMetricService,
GRAPH_PREVIEW,
uiMetricService,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
import { TestProviders } from '../../../../common/mock';
@ -36,15 +36,6 @@ jest.mock('@kbn/cloud-security-posture-common/utils/ui_metrics', () => ({
const uiMetricServiceMock = uiMetricService as jest.Mocked<typeof uiMetricService>;
const mockUseUiSetting = jest.fn().mockReturnValue([true]);
jest.mock('@kbn/kibana-react-plugin/public', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public');
return {
...original,
useUiSetting$: () => mockUseUiSetting(),
};
});
jest.mock('../../../../common/hooks/use_experimental_features', () => ({
useIsExperimentalFeatureEnabled: jest.fn(),
}));
@ -355,58 +346,6 @@ describe('<GraphPreviewContainer />', () => {
});
});
it('should render component and without link in header when expanding flyout feature is disabled', async () => {
mockUseUiSetting.mockReturnValue([false]);
mockUseFetchGraphData.mockReturnValue({
isLoading: false,
isError: false,
data: { nodes: DEFAULT_NODES, edges: [] },
});
const timestamp = new Date().toISOString();
(useGraphPreview as jest.Mock).mockReturnValue({
timestamp,
eventIds: [],
hasGraphRepresentation: true,
isAlert: true,
});
const { getByTestId, queryByTestId, findByTestId } = renderGraphPreview();
// Using findByTestId to wait for the component to be rendered because it is a lazy loaded component
expect(await findByTestId(GRAPH_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(mockUseFetchGraphData).toHaveBeenCalled();
expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({
req: {
query: {
originEventIds: [],
start: `${timestamp}||-30m`,
end: `${timestamp}||+30m`,
},
},
options: {
enabled: true,
refetchOnWindowFocus: false,
},
});
});
it('should not render when graph data is not available', async () => {
mockUseFetchGraphData.mockReturnValue({
isLoading: false,
@ -429,9 +368,6 @@ describe('<GraphPreviewContainer />', () => {
expect(
await findByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
@ -441,9 +377,6 @@ describe('<GraphPreviewContainer />', () => {
expect(
getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(GRAPH_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(mockUseFetchGraphData).toHaveBeenCalled();
expect(mockUseFetchGraphData.mock.calls[0][0]).toEqual({
req: {

View file

@ -5,18 +5,16 @@
* 2.0.
*/
import React, { useEffect } from 'react';
import React, { useEffect, useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { EuiBetaBadge, useGeneratedHtmlId } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useFetchGraphData } from '@kbn/cloud-security-posture-graph/src/hooks';
import {
uiMetricService,
GRAPH_PREVIEW,
uiMetricService,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../../common/constants';
import { useDocumentDetailsContext } from '../../shared/context';
import { GRAPH_PREVIEW_TEST_ID } from './test_ids';
import { GraphPreview } from './graph_preview';
@ -40,10 +38,10 @@ export const GraphPreviewContainer: React.FC = () => {
dataFormattedForFieldBrowser,
} = useDocumentDetailsContext();
const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>(
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING
const allowFlyoutExpansion = useMemo(
() => !isPreviewMode && !isPreview,
[isPreview, isPreviewMode]
);
const allowFlyoutExpansion = visualizationInFlyoutEnabled && !isPreviewMode && !isPreview;
const { navigateToGraphVisualization } = useNavigateToGraphVisualization({
eventId,

View file

@ -36,15 +36,6 @@ jest.mock('../../shared/hooks/use_navigate_to_session_view', () => {
return { useNavigateToSessionView: () => ({ navigateToSessionView: mockNavigateToSessionView }) };
});
const mockUseUiSetting = jest.fn().mockReturnValue([false]);
jest.mock('@kbn/kibana-react-plugin/public', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public');
return {
...original,
useUiSetting$: () => mockUseUiSetting(),
};
});
jest.mock('react-redux', () => {
const original = jest.requireActual('react-redux');
@ -75,140 +66,81 @@ const renderSessionPreview = (context = mockContextValue) =>
);
describe('SessionPreviewContainer', () => {
beforeEach(() => {
jest.clearAllMocks();
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
});
it('should render component and link in header', () => {
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
const { getByTestId } = renderSessionPreview();
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
screen.queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
expect(
screen.getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
screen.getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
screen.queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
expect(
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toHaveTextContent(NO_DATA_MESSAGE);
expect(
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toHaveTextContent(UPSELL_TEXT);
});
it('should render error message and text in header if no sessionConfig', () => {
(useSessionViewConfig as jest.Mock).mockReturnValue(null);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
const { getByTestId, queryByTestId } = renderSessionPreview();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
screen.getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toHaveTextContent(NO_DATA_MESSAGE);
expect(
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toHaveTextContent(UPSELL_TEXT);
expect(queryByTestId(SESSION_PREVIEW_TEST_ID)).not.toBeInTheDocument();
});
it('should render upsell message in header if no correct license', () => {
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => false });
const { getByTestId, queryByTestId } = renderSessionPreview();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
screen.getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toHaveTextContent(UPSELL_TEXT);
expect(
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toHaveTextContent(NO_DATA_MESSAGE);
expect(queryByTestId(SESSION_PREVIEW_TEST_ID)).not.toBeInTheDocument();
});
it('should not render link to session viewer if flyout is open in preview', () => {
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
const { getByTestId, queryByTestId } = renderSessionPreview({
...mockContextValue,
isPreview: true,
describe('when newExpandableFlyoutNavigationDisabled is true', () => {
beforeEach(() => {
jest.clearAllMocks();
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
(useInvestigateInTimeline as jest.Mock).mockReturnValue({
investigateInTimelineAlertClick: jest.fn(),
});
});
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
it('should not render link to session viewer if flyout is open in preview mode', () => {
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
const { getByTestId, queryByTestId } = renderSessionPreview({
...mockContextValue,
isPreviewMode: true,
});
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
describe('when visualization in flyout flag is enabled', () => {
it('should open left panel vizualization tab when visualization in flyout flag is on', () => {
mockUseUiSetting.mockReturnValue([true]);
it('should render component and link in header', () => {
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
const { getByTestId } = renderSessionPreview();
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID)).click();
expect(mockNavigateToSessionView).toHaveBeenCalled();
expect(
screen.queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
expect(
screen.getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
screen.getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
screen.queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
expect(
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toHaveTextContent(NO_DATA_MESSAGE);
expect(
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toHaveTextContent(UPSELL_TEXT);
});
it('should render error message and text in header if no sessionConfig', () => {
(useSessionViewConfig as jest.Mock).mockReturnValue(null);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
const { getByTestId, queryByTestId } = renderSessionPreview();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
screen.getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toHaveTextContent(NO_DATA_MESSAGE);
expect(
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toHaveTextContent(UPSELL_TEXT);
expect(queryByTestId(SESSION_PREVIEW_TEST_ID)).not.toBeInTheDocument();
});
it('should render upsell message in header if no correct license', () => {
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => false });
const { getByTestId, queryByTestId } = renderSessionPreview();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
screen.getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toHaveTextContent(UPSELL_TEXT);
expect(
screen.queryByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toHaveTextContent(NO_DATA_MESSAGE);
expect(queryByTestId(SESSION_PREVIEW_TEST_ID)).not.toBeInTheDocument();
});
it('should not render link to session viewer if flyout is open in rule preview', () => {
@ -221,6 +153,15 @@ describe('SessionPreviewContainer', () => {
});
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
@ -246,104 +187,68 @@ describe('SessionPreviewContainer', () => {
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
it('should open left panel visualization tab when visualization in flyout flag is on', () => {
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
const { getByTestId } = renderSessionPreview();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID)).click();
expect(mockNavigateToSessionView).toHaveBeenCalled();
});
});
describe('when newExpandableFlyoutNavigationDisabled is false', () => {
describe('when visualization in flyout flag is enabled', () => {
beforeEach(() => {
jest.clearAllMocks();
mockUseUiSetting.mockReturnValue([true]);
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
});
it('should open left panel vizualization tab when visualization in flyout flag is on', () => {
const { getByTestId } = renderSessionPreview();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID)).click();
expect(mockNavigateToSessionView).toHaveBeenCalled();
});
it('should not render link to session viewer if flyout is open in rule preview', () => {
const { getByTestId, queryByTestId } = renderSessionPreview({
...mockContextValue,
isPreview: true,
});
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
it('should render link to session viewer if flyout is open in preview mode', () => {
const { getByTestId } = renderSessionPreview({
...mockContextValue,
isPreviewMode: true,
});
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID)).click();
expect(mockNavigateToSessionView).toHaveBeenCalled();
});
beforeEach(() => {
jest.clearAllMocks();
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
});
describe('when visualization in flyout flag is not enabled', () => {
beforeEach(() => {
jest.clearAllMocks();
mockUseUiSetting.mockReturnValue([false]);
(useSessionViewConfig as jest.Mock).mockReturnValue(sessionViewConfig);
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
it('should open left panel visualization tab when visualization in flyout flag is on', () => {
const { getByTestId } = renderSessionPreview();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID)).click();
expect(mockNavigateToSessionView).toHaveBeenCalled();
});
it('should not render link to session viewer if flyout is open in rule preview', () => {
const { getByTestId, queryByTestId } = renderSessionPreview({
...mockContextValue,
isPreview: true,
});
it('should open session viewer in timeline', () => {
const { getByTestId } = renderSessionPreview();
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({});
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID)).click();
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
expect(investigateInTimelineAlertClick).toHaveBeenCalled();
it('should render link to session viewer if flyout is open in preview mode', () => {
const { getByTestId } = renderSessionPreview({
...mockContextValue,
isPreviewMode: true,
});
it('should not render link to session viewer if flyout is open in rule preview', () => {
const { getByTestId, queryByTestId } = renderSessionPreview({
...mockContextValue,
isPreview: true,
});
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument();
expect(
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID))
).toBeInTheDocument();
expect(
queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID))
).not.toBeInTheDocument();
});
it('should render link to open session viewer in timeline if flyout is open in preview mode', () => {
const { getByTestId } = renderSessionPreview({
...mockContextValue,
isPreviewMode: true,
});
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({});
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID)).click();
expect(investigateInTimelineAlertClick).toHaveBeenCalled();
});
getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID)).click();
expect(mockNavigateToSessionView).toHaveBeenCalled();
});
});
});

View file

@ -5,29 +5,18 @@
* 2.0.
*/
import React, { type FC, useCallback, useMemo } from 'react';
import { TimelineTabs } from '@kbn/securitysolution-data-table';
import { useDispatch } from 'react-redux';
import React, { type FC, useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../../common/constants';
import { useLicense } from '../../../../common/hooks/use_license';
import { SessionPreview } from './session_preview';
import { useSessionViewConfig } from '../../shared/hooks/use_session_view_config';
import { useInvestigateInTimeline } from '../../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline';
import { useDocumentDetailsContext } from '../../shared/context';
import { ALERTS_ACTIONS } from '../../../../common/lib/apm/user_actions';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
import { SESSION_PREVIEW_TEST_ID } from './test_ids';
import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction';
import { setActiveTabTimeline } from '../../../../timelines/store/actions';
import { getScopedActions } from '../../../../helpers';
import { useNavigateToSessionView } from '../../shared/hooks/use_navigate_to_session_view';
import { SessionViewNoDataMessage } from '../../shared/components/session_view_no_data_message';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
const timelineId = 'timeline-1';
/**
* Checks if the SessionView component is available, if so render it or else render an error message
*/
@ -36,17 +25,12 @@ export const SessionPreviewContainer: FC = () => {
eventId,
indexName,
scopeId,
dataAsNestedObject,
getFieldsData,
isPreview,
isPreviewMode,
dataFormattedForFieldBrowser,
} = useDocumentDetailsContext();
const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>(
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING
);
// decide whether to show the session view or not
const sessionViewConfig = useSessionViewConfig({ getFieldsData, dataFormattedForFieldBrowser });
const isEnterprisePlus = useLicense().isEnterprise();
@ -56,33 +40,6 @@ export const SessionPreviewContainer: FC = () => {
'newExpandableFlyoutNavigationDisabled'
);
const dispatch = useDispatch();
const { startTransaction } = useStartTransaction();
const scopedActions = getScopedActions(timelineId);
const { investigateInTimelineAlertClick } = useInvestigateInTimeline({
ecsRowData: dataAsNestedObject,
});
const goToSessionViewTab = useCallback(async () => {
// open timeline
await investigateInTimelineAlertClick();
// open session view tab
startTransaction({ name: ALERTS_ACTIONS.OPEN_SESSION_VIEW });
if (sessionViewConfig !== null) {
dispatch(setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.session }));
if (scopedActions) {
dispatch(scopedActions.updateSessionViewConfig({ id: timelineId, sessionViewConfig }));
}
}
}, [
dispatch,
investigateInTimelineAlertClick,
scopedActions,
sessionViewConfig,
startTransaction,
]);
const { navigateToSessionView } = useNavigateToSessionView({
eventId,
indexName,
@ -91,10 +48,7 @@ export const SessionPreviewContainer: FC = () => {
isPreviewMode,
});
const iconType = useMemo(() => {
const icon = visualizationInFlyoutEnabled ? 'arrowStart' : 'timeline';
return !isPreviewMode ? icon : undefined;
}, [visualizationInFlyoutEnabled, isPreviewMode]);
const iconType = useMemo(() => (!isPreviewMode ? 'arrowStart' : undefined), [isPreviewMode]);
const isNavigationEnabled = useMemo(() => {
// if the session view is not enabled or in rule preview mode, the navigation is not enabled
@ -121,7 +75,7 @@ export const SessionPreviewContainer: FC = () => {
iconType,
...(isNavigationEnabled && {
link: {
callback: visualizationInFlyoutEnabled ? navigateToSessionView : goToSessionViewTab,
callback: navigateToSessionView,
tooltip: (
<FormattedMessage
id="xpack.securitySolution.flyout.right.visualizations.sessionPreview.sessionPreviewTooltip"

View file

@ -10,14 +10,14 @@ import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { render } from '@testing-library/react';
import { useFetchGraphData } from '@kbn/cloud-security-posture-graph/src/hooks';
import {
uiMetricService,
GRAPH_PREVIEW,
uiMetricService,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
import {
ANALYZER_PREVIEW_TEST_ID,
SESSION_PREVIEW_TEST_ID,
GRAPH_PREVIEW_TEST_ID,
SESSION_PREVIEW_TEST_ID,
VISUALIZATIONS_SECTION_CONTENT_TEST_ID,
VISUALIZATIONS_SECTION_HEADER_TEST_ID,
} from './test_ids';
@ -34,10 +34,7 @@ import { useIsInvestigateInResolverActionEnabled } from '../../../../detections/
import { useGraphPreview } from '../../shared/hooks/use_graph_preview';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { createUseUiSetting$Mock } from '../../../../common/lib/kibana/kibana_react.mock';
import {
ENABLE_GRAPH_VISUALIZATION_SETTING,
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING,
} from '../../../../../common/constants';
import { ENABLE_GRAPH_VISUALIZATION_SETTING } from '../../../../../common/constants';
jest.mock('../hooks/use_expand_section');
jest.mock('../../shared/hooks/use_alert_prevalence_from_process_tree', () => ({
@ -70,8 +67,8 @@ jest.mock('../../../../common/hooks/use_experimental_features', () => ({
const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock;
useIsExperimentalFeatureEnabledMock.mockReturnValue(true);
const mockUseUiSetting = jest.fn().mockImplementation((key) => [false]);
const mockUseUiSetting = jest.fn().mockImplementation((key) => [false]);
jest.mock('@kbn/kibana-react-plugin/public', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public');
return {
@ -163,8 +160,6 @@ describe('<VisualizationsSection />', () => {
if (key === ENABLE_GRAPH_VISUALIZATION_SETTING) {
return [false, jest.fn()];
} else if (key === ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING) {
return [false, jest.fn()];
}
return useUiSetting$Mock(key, defaultValue);
@ -185,8 +180,6 @@ describe('<VisualizationsSection />', () => {
if (key === ENABLE_GRAPH_VISUALIZATION_SETTING) {
return [true, jest.fn()];
} else if (key === ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING) {
return [true, jest.fn()];
}
return useUiSetting$Mock(key, defaultValue);
@ -208,27 +201,6 @@ describe('<VisualizationsSection />', () => {
if (key === ENABLE_GRAPH_VISUALIZATION_SETTING) {
return [false, jest.fn()];
} else if (key === ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING) {
return [true, jest.fn()];
}
return useUiSetting$Mock(key, defaultValue);
});
const { queryByTestId } = renderVisualizationsSection();
expect(queryByTestId(`${GRAPH_PREVIEW_TEST_ID}LeftSection`)).not.toBeInTheDocument();
});
it('should not render the graph preview component if the flyout feature is disabled', () => {
(useExpandSection as jest.Mock).mockReturnValue(true);
mockUseUiSetting.mockImplementation((key, defaultValue) => {
const useUiSetting$Mock = createUseUiSetting$Mock();
if (key === ENABLE_GRAPH_VISUALIZATION_SETTING) {
return [true, jest.fn()];
} else if (key === ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING) {
return [false, jest.fn()];
}
return useUiSetting$Mock(key, defaultValue);

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { memo } from 'react';
import React, { memo, useMemo } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
@ -17,10 +17,7 @@ import { VISUALIZATIONS_TEST_ID } from './test_ids';
import { GraphPreviewContainer } from './graph_preview_container';
import { useDocumentDetailsContext } from '../../shared/context';
import { useGraphPreview } from '../../shared/hooks/use_graph_preview';
import {
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING,
ENABLE_GRAPH_VISUALIZATION_SETTING,
} from '../../../../../common/constants';
import { ENABLE_GRAPH_VISUALIZATION_SETTING } from '../../../../../common/constants';
const KEY = 'visualizations';
@ -32,10 +29,6 @@ export const VisualizationsSection = memo(() => {
const { dataAsNestedObject, getFieldsData, dataFormattedForFieldBrowser } =
useDocumentDetailsContext();
const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>(
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING
);
const [graphVisualizationEnabled] = useUiSetting$<boolean>(ENABLE_GRAPH_VISUALIZATION_SETTING);
// Decide whether to show the graph preview or not
@ -45,8 +38,10 @@ export const VisualizationsSection = memo(() => {
dataFormattedForFieldBrowser,
});
const shouldShowGraphPreview =
visualizationInFlyoutEnabled && graphVisualizationEnabled && hasGraphRepresentation;
const shouldShowGraphPreview = useMemo(
() => graphVisualizationEnabled && hasGraphRepresentation,
[graphVisualizationEnabled, hasGraphRepresentation]
);
return (
<ExpandableSection

View file

@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n';
import { DetailPanelAlertTab, useFetchSessionViewAlerts } from '@kbn/session-view-plugin/public';
import type { ProcessEvent } from '@kbn/session-view-plugin/common';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { LeftPanelVisualizeTab } from '../../left';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { SESSION_VIEW_ID } from '../../left/components/session_view';
import {
@ -22,6 +23,7 @@ import { useSessionViewPanelContext } from '../context';
import { SourcererScopeName } from '../../../../sourcerer/store/model';
import { useSourcererDataView } from '../../../../sourcerer/containers';
import { useSelectedPatterns } from '../../../../data_view_manager/hooks/use_selected_patterns';
/**
* Tab displayed in the SessionView preview panel, shows alerts related to the session.
*/
@ -106,7 +108,7 @@ export const AlertsTab = memo(() => {
jumpToCursor,
},
path: {
tab: 'visualize',
tab: LeftPanelVisualizeTab,
subTab: SESSION_VIEW_ID,
},
});

View file

@ -9,6 +9,7 @@ import { useCallback, useMemo } from 'react';
import type { FlyoutPanelProps } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import type { Maybe } from '@kbn/timelines-plugin/common/search_strategy/common';
import { LeftPanelVisualizeTab } from '../../left';
import { useKibana } from '../../../../common/lib/kibana';
import { ANALYZE_GRAPH_ID } from '../../left/components/analyze_graph';
import { DocumentDetailsLeftPanelKey, DocumentDetailsRightPanelKey } from '../constants/panel_keys';
@ -84,7 +85,7 @@ export const useNavigateToAnalyzer = ({
scopeId,
},
path: {
tab: 'visualize',
tab: LeftPanelVisualizeTab,
subTab: ANALYZE_GRAPH_ID,
},
}),
@ -98,7 +99,7 @@ export const useNavigateToAnalyzer = ({
telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutTabClicked, {
location: scopeId,
panel: 'left',
tabId: 'visualize',
tabId: LeftPanelVisualizeTab,
});
}
// if flyout is not currently open, open flyout with right, left and preview panel

View file

@ -9,6 +9,7 @@ import { useCallback, useMemo } from 'react';
import type { FlyoutPanelProps } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import type { Maybe } from '@kbn/timelines-plugin/common/search_strategy/common';
import { LeftPanelVisualizeTab } from '../../left';
import { useKibana } from '../../../../common/lib/kibana';
import { DocumentDetailsLeftPanelKey, DocumentDetailsRightPanelKey } from '../constants/panel_keys';
import { DocumentEventTypes } from '../../../../common/lib/telemetry';
@ -74,7 +75,7 @@ export const useNavigateToGraphVisualization = ({
scopeId,
},
path: {
tab: 'visualize',
tab: LeftPanelVisualizeTab,
subTab: GRAPH_ID,
},
}),
@ -87,7 +88,7 @@ export const useNavigateToGraphVisualization = ({
telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutTabClicked, {
location: scopeId,
panel: 'left',
tabId: 'visualize',
tabId: LeftPanelVisualizeTab,
});
} else {
openFlyout({

View file

@ -9,6 +9,7 @@ import { useCallback, useMemo } from 'react';
import type { FlyoutPanelProps } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import type { Maybe } from '@kbn/timelines-plugin/common/search_strategy/common';
import { LeftPanelVisualizeTab } from '../../left';
import { useKibana } from '../../../../common/lib/kibana';
import { SESSION_VIEW_ID } from '../../left/components/session_view';
import { DocumentDetailsLeftPanelKey, DocumentDetailsRightPanelKey } from '../constants/panel_keys';
@ -84,7 +85,7 @@ export const useNavigateToSessionView = ({
scopeId,
},
path: {
tab: 'visualize',
tab: LeftPanelVisualizeTab,
subTab: SESSION_VIEW_ID,
},
}),
@ -98,7 +99,7 @@ export const useNavigateToSessionView = ({
telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutTabClicked, {
location: scopeId,
panel: 'left',
tabId: 'visualize',
tabId: LeftPanelVisualizeTab,
});
}
// if flyout is not currently open, open flyout with right and left panels

View file

@ -18,10 +18,11 @@ import { timelineDefaults } from '../../store/defaults';
import type { CellValueElementProps } from './cell_rendering';
import { SourcererScopeName } from '../../../sourcerer/store/model';
import { TimelineModalHeader } from '../modal/header';
import type { TimelineId, RowRenderer } from '../../../../common/types/timeline';
import type { RowRenderer, TimelineId } from '../../../../common/types/timeline';
import { TimelineTypeEnum } from '../../../../common/api/timeline';
import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector';
import type { State } from '../../../common/store';
import { sourcererSelectors } from '../../../common/store';
import { EVENTS_COUNT_BUTTON_CLASS_NAME, onTimelineTabKeyPressed } from './helpers';
import * as i18n from './translations';
import { TabsContent } from './tabs';
@ -29,7 +30,6 @@ import { HideShowContainer, TimelineContainer } from './styles';
import { useTimelineFullScreen } from '../../../common/containers/use_full_screen';
import { EXIT_FULL_SCREEN_CLASS_NAME } from '../../../common/components/exit_full_screen';
import { useResolveConflict } from '../../../common/hooks/use_resolve_conflict';
import { sourcererSelectors } from '../../../common/store';
import { defaultUdtHeaders } from './body/column_headers/default_headers';
import { useSelectedPatterns } from '../../../data_view_manager/hooks/use_selected_patterns';
import { useDataViewSpec } from '../../../data_view_manager/hooks/use_data_view_spec';
@ -82,7 +82,6 @@ const StatefulTimelineComponent: React.FC<Props> = ({
savedObjectId,
timelineType,
description,
sessionViewConfig,
initialized,
} = useDeepEqualSelector((state) =>
pick(
@ -93,7 +92,6 @@ const StatefulTimelineComponent: React.FC<Props> = ({
'savedObjectId',
'timelineType',
'description',
'sessionViewConfig',
'initialized',
'show',
'activeTab',
@ -230,7 +228,6 @@ const StatefulTimelineComponent: React.FC<Props> = ({
<TabsContent
graphEventId={graphEventId}
sessionViewConfig={sessionViewConfig}
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
timelineId={timelineId}

View file

@ -115,16 +115,6 @@ describe('Timeline', () => {
describe('analyzer tab and session view tab', () => {
const analyzerTabSubj = `timelineTabs-${TimelineTabs.graph}`;
const sessionViewTabSubj = `timelineTabs-${TimelineTabs.session}`;
it('should show the analyzer tab when the advanced setting is disabled', () => {
(useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true });
render(
<TestProviders>
<TabsContent {...defaultProps} />
</TestProviders>
);
expect(screen.getByTestId(analyzerTabSubj)).toBeInTheDocument();
expect(screen.getByTestId(sessionViewTabSubj)).toBeInTheDocument();
});
it('should not show the analyzer tab when the advanced setting is enabled', async () => {
mockUseUiSetting.mockReturnValue([true]);

View file

@ -5,24 +5,22 @@
* 2.0.
*/
import { EuiBadge, EuiSkeletonText, EuiTabs, EuiTab } from '@elastic/eui';
import { EuiBadge, EuiSkeletonText, EuiTab, EuiTabs } from '@elastic/eui';
import { isEmpty } from 'lodash/fp';
import type { Ref, ReactElement, ComponentType } from 'react';
import type { ComponentType, ReactElement, Ref } from 'react';
import React, { lazy, memo, Suspense, useCallback, useEffect, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import type { State } from '../../../../common/store';
import { useEsqlAvailability } from '../../../../common/hooks/esql/use_esql_availability';
import type { SessionViewConfig } from '../../../../../common/types';
import type { RowRenderer, TimelineId } from '../../../../../common/types/timeline';
import { TimelineTabs } from '../../../../../common/types/timeline';
import { type TimelineType, TimelineTypeEnum } from '../../../../../common/api/timeline';
import {
useShallowEqualSelector,
useDeepEqualSelector,
useShallowEqualSelector,
} from '../../../../common/hooks/use_selector';
import {
EqlEventsCountBadge,
@ -32,18 +30,16 @@ import { timelineActions } from '../../../store';
import type { CellValueElementProps } from '../cell_rendering';
import {
getActiveTabSelector,
getEventIdToNoteIdsSelector,
getNoteIdsSelector,
getNotesSelector,
getPinnedEventSelector,
getShowTimelineSelector,
getEventIdToNoteIdsSelector,
} from './selectors';
import * as i18n from './translations';
import { useLicense } from '../../../../common/hooks/use_license';
import { initializeTimelineSettings } from '../../../store/actions';
import { selectTimelineById, selectTimelineESQLSavedSearchId } from '../../../store/selectors';
import { fetchNotesBySavedObjectIds, makeSelectNotesBySavedObjectId } from '../../../../notes';
import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../../common/constants';
import { useUserPrivileges } from '../../../../common/components/user_privileges';
import { LazyTimelineTabRenderer, TimelineTabFallback } from './lazy_timeline_tab_renderer';
@ -102,7 +98,6 @@ interface BasicTimelineTab {
timelineId: TimelineId;
timelineType: TimelineType;
graphEventId?: string;
sessionViewConfig?: SessionViewConfig | null;
timelineDescription: string;
}
@ -243,7 +238,6 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({
timelineFullScreen,
timelineType,
graphEventId,
sessionViewConfig,
timelineDescription,
}) => {
const dispatch = useDispatch();
@ -263,10 +257,6 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({
'securitySolutionNotesDisabled'
);
const [visualizationInFlyoutEnabled] = useUiSetting$<boolean>(
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING
);
const activeTab = useShallowEqualSelector((state) => getActiveTab(state, timelineId));
const showTimeline = useShallowEqualSelector((state) => getShowTimeline(state, timelineId));
const shouldShowESQLTab = useMemo(
@ -285,8 +275,6 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({
);
const appNotes = useDeepEqualSelector((state) => getAppNotes(state));
const isEnterprisePlus = useLicense().isEnterprise();
// old notes system (through timeline)
const allTimelineNoteIds = useMemo(() => {
const eventNoteIds = Object.values(eventIdToNoteIds).reduce<string[]>(
@ -356,10 +344,6 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({
setActiveTab(TimelineTabs.eql);
}, [setActiveTab]);
const setGraphAsActiveTab = useCallback(() => {
setActiveTab(TimelineTabs.graph);
}, [setActiveTab]);
const setNotesAsActiveTab = useCallback(() => {
setActiveTab(TimelineTabs.notes);
}, [setActiveTab]);
@ -368,10 +352,6 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({
setActiveTab(TimelineTabs.pinned);
}, [setActiveTab]);
const setSessionAsActiveTab = useCallback(() => {
setActiveTab(TimelineTabs.session);
}, [setActiveTab]);
const setEsqlAsActiveTab = useCallback(() => {
dispatch(
initializeTimelineSettings({
@ -424,28 +404,6 @@ const TabsContentComponent: React.FC<BasicTimelineTab> = ({
{showTimeline && <EqlEventsCountBadge />}
</StyledEuiTab>
)}
{!visualizationInFlyoutEnabled && (
<EuiTab
data-test-subj={`timelineTabs-${TimelineTabs.graph}`}
onClick={setGraphAsActiveTab}
isSelected={activeTab === TimelineTabs.graph}
disabled={!graphEventId}
key={TimelineTabs.graph}
>
{i18n.ANALYZER_TAB}
</EuiTab>
)}
{isEnterprisePlus && !visualizationInFlyoutEnabled && (
<EuiTab
data-test-subj={`timelineTabs-${TimelineTabs.session}`}
onClick={setSessionAsActiveTab}
isSelected={activeTab === TimelineTabs.session}
disabled={sessionViewConfig === null}
key={TimelineTabs.session}
>
{i18n.SESSION_TAB}
</EuiTab>
)}
<StyledEuiTab
data-test-subj={`timelineTabs-${TimelineTabs.notes}`}
onClick={setNotesAsActiveTab}

View file

@ -18,13 +18,6 @@ export const EQL_TAB = i18n.translate('xpack.securitySolution.timeline.tabs.eqlT
defaultMessage: 'Correlation',
});
export const ANALYZER_TAB = i18n.translate(
'xpack.securitySolution.timeline.tabs.analyserTabTimelineTitle',
{
defaultMessage: 'Analyzer',
}
);
export const NOTES_TAB = i18n.translate(
'xpack.securitySolution.timeline.tabs.notesTabTimelineTitle',
{
@ -39,23 +32,9 @@ export const PINNED_TAB = i18n.translate(
}
);
export const SECURITY_ASSISTANT = i18n.translate(
'xpack.securitySolution.timeline.tabs.securityAssistantTimelineTitle',
{
defaultMessage: 'Elastic AI Assistant',
}
);
export const DISCOVER_ESQL_IN_TIMELINE_TAB = i18n.translate(
'xpack.securitySolution.timeline.tabs.discoverEsqlInTimeline',
{
defaultMessage: 'ES|QL',
}
);
export const SESSION_TAB = i18n.translate(
'xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle',
{
defaultMessage: 'Session View',
}
);

View file

@ -12,6 +12,9 @@ import type { CoreSetup, UiSettingsParams } from '@kbn/core/server';
import type { Connector } from '@kbn/actions-plugin/server/application/connector/types';
import {
APP_ID,
DEFAULT_AI_CONNECTOR,
DEFAULT_ALERT_TAGS_KEY,
DEFAULT_ALERT_TAGS_VALUE,
DEFAULT_ANOMALY_SCORE,
DEFAULT_APP_REFRESH_INTERVAL,
DEFAULT_APP_TIME_RANGE,
@ -26,23 +29,19 @@ import {
DEFAULT_THREAT_INDEX_KEY,
DEFAULT_THREAT_INDEX_VALUE,
DEFAULT_TO,
ENABLE_ASSET_INVENTORY_SETTING,
ENABLE_CCS_READ_WARNING_SETTING,
ENABLE_GRAPH_VISUALIZATION_SETTING,
ENABLE_NEWS_FEED_SETTING,
EXCLUDE_COLD_AND_FROZEN_TIERS_IN_ANALYZER,
EXCLUDED_DATA_TIERS_FOR_RULE_EXECUTION,
EXTENDED_RULE_EXECUTION_LOGGING_ENABLED_SETTING,
EXTENDED_RULE_EXECUTION_LOGGING_MIN_LEVEL_SETTING,
IP_REPUTATION_LINKS_SETTING,
IP_REPUTATION_LINKS_SETTING_DEFAULT,
NEWS_FEED_URL_SETTING,
NEWS_FEED_URL_SETTING_DEFAULT,
ENABLE_CCS_READ_WARNING_SETTING,
SHOW_RELATED_INTEGRATIONS_SETTING,
EXTENDED_RULE_EXECUTION_LOGGING_ENABLED_SETTING,
EXTENDED_RULE_EXECUTION_LOGGING_MIN_LEVEL_SETTING,
DEFAULT_ALERT_TAGS_KEY,
DEFAULT_ALERT_TAGS_VALUE,
EXCLUDE_COLD_AND_FROZEN_TIERS_IN_ANALYZER,
EXCLUDED_DATA_TIERS_FOR_RULE_EXECUTION,
ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING,
ENABLE_GRAPH_VISUALIZATION_SETTING,
ENABLE_ASSET_INVENTORY_SETTING,
DEFAULT_AI_CONNECTOR,
} from '../common/constants';
import type { ExperimentalFeatures } from '../common/experimental_features';
import { LogLevelSetting } from '../common/api/detection_engine/rule_monitoring';
@ -66,12 +65,6 @@ export const initUiSettings = (
experimentalFeatures: ExperimentalFeatures,
validationsEnabled: boolean
) => {
const enableVisualizationsInFlyoutLabel = i18n.translate(
'xpack.securitySolution.uiSettings.enableVisualizationsInFlyoutLabel',
{
defaultMessage: 'Enable visualizations in flyout',
}
);
const securityUiSettings: Record<string, UiSettingsParams<unknown>> = {
[DEFAULT_APP_REFRESH_INTERVAL]: {
type: 'json',
@ -215,22 +208,6 @@ export const initUiSettings = (
schema: schema.boolean(),
solution: 'security',
},
[ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING]: {
name: enableVisualizationsInFlyoutLabel,
value: true,
description: i18n.translate(
'xpack.securitySolution.uiSettings.enableVisualizationsInFlyoutDescription',
{
defaultMessage:
'Enable visualizations (analyzer and session viewer) in document details flyout.',
}
),
type: 'boolean',
category: [APP_ID],
requiresPageReload: true,
schema: schema.boolean(),
solution: 'security',
},
[ENABLE_GRAPH_VISUALIZATION_SETTING]: {
name: i18n.translate('xpack.securitySolution.uiSettings.enableGraphVisualizationLabel', {
defaultMessage: 'Enable graph visualization',
@ -238,13 +215,8 @@ export const initUiSettings = (
description: i18n.translate(
'xpack.securitySolution.uiSettings.enableGraphVisualizationDescription',
{
defaultMessage: `<em>[technical preview]</em> Enable the Graph Visualization feature within the Security Solution.
<br/>Note: This feature requires the {visualizationFlyoutFeatureFlag} setting to be enabled.
<br/>Please ensure both settings are enabled to use graph visualizations in flyout.`,
values: {
em: (chunks) => `<em>${chunks}</em>`,
visualizationFlyoutFeatureFlag: `<code>${enableVisualizationsInFlyoutLabel}</code>`,
},
defaultMessage: `<em>[technical preview]</em> Enable the Graph Visualization feature within the Security Solution.`,
values: { em: (chunks) => `<em>${chunks}</em>` },
}
),
type: 'boolean',

View file

@ -60,7 +60,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
* 1. release a new package to EPR
* 2. merge the updated version number change to kibana
*/
`--uiSettings.overrides.securitySolution:enableVisualizationsInFlyout=true`,
`--uiSettings.overrides.securitySolution:enableGraphVisualization=true`,
`--xpack.fleet.packages.0.name=cloud_security_posture`,
`--xpack.fleet.packages.0.version=${CLOUD_SECURITY_PLUGIN_VERSION}`,