From a612673b96ba5414a461540d6df8205141e55415 Mon Sep 17 00:00:00 2001
From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com>
Date: Tue, 12 Nov 2019 20:54:31 -0500
Subject: [PATCH] [SIEM] Detection engine placeholders (#50220)
* attempt at getting nav working
* fix detection-engine href redirect issue
* rough out basic page routing
* kql placeholder
* update page and panel headers
* rough out card table poc styles
* change HeaderPanel to HeaderSection
* cleanup and unit test updates
* rough out utilityBar poc
* clean up UtilityBar naming and styles
* support popovers in utility bar
* refactor icon side
* UtilityBar unit tests
* remove page tests for now
* adjust routes
* add comment
* cleanup chart
* open/closed signals content toggle
* remove EuiFilterButton icons
* fix misaligned popover button
* add split prop for HeaderSection
* fleshing out activity monitor table
* update global header to include logo
* fix tests
* correct table types; thanks Garrett!
* LinkIcon comp poc
* fix bugs, errors, tests
* rm import
* table cleanup
* correct merge errors
* switch All Rules to EuiBasicTable
* expand table types and values
* fleshing out all rules table
* rough out rule details
* move chart to separate comp
* update supplement layout
* example rule fail
* switch to new discover-style search
* add ProgressInline comp
* update unit tests and snapshots
* cleanup
* correct merge weirdness
* move text styles to all subtitle items
* correct invalid nav markup; update tests; cleanup
* fix console errors
* add empty page
* change to EuiButtonEmpty in HeaderGlobal
* overflow popover
* rough out edit layout
* new WrapperPage comp POC
* cleanup
* var for timeline gutter
* tests and snapshots update
* fix type + review + re-arrange code
* adding feature flag + fix route issue
* fix type with unit test
* Removing unused translation
---
.../integration/lib/navigation/selectors.ts | 8 +-
.../__snapshots__/utility_bar.test.tsx.snap | 36 +
.../utility_bar_action.test.tsx.snap | 11 +
.../utility_bar_group.test.tsx.snap | 11 +
.../utility_bar_section.test.tsx.snap | 13 +
.../utility_bar_text.test.tsx.snap | 9 +
.../detection_engine/utility_bar/index.ts | 11 +
.../detection_engine/utility_bar/styles.tsx | 118 ++
.../utility_bar/utility_bar.test.tsx | 113 ++
.../utility_bar/utility_bar.tsx | 18 +
.../utility_bar/utility_bar_action.test.tsx | 45 +
.../utility_bar/utility_bar_action.tsx | 72 ++
.../utility_bar/utility_bar_group.test.tsx | 29 +
.../utility_bar/utility_bar_group.tsx | 18 +
.../utility_bar/utility_bar_section.test.tsx | 31 +
.../utility_bar/utility_bar_section.tsx | 18 +
.../utility_bar/utility_bar_text.test.tsx | 27 +
.../utility_bar/utility_bar_text.tsx | 18 +
.../events_viewer/events_viewer.test.tsx | 2 +-
.../events_viewer/events_viewer.tsx | 4 +-
.../filters_global/filters_global.tsx | 21 +-
.../__snapshots__/index.test.tsx.snap | 7 +
.../components/header_global/index.test.tsx | 34 +
.../public/components/header_global/index.tsx | 82 ++
.../components/header_global/translations.ts | 15 +
.../__snapshots__/header_page.test.tsx.snap | 14 -
.../__snapshots__/index.test.tsx.snap | 23 +
.../header_page/header_page.test.tsx | 42 -
.../components/header_page/header_page.tsx | 78 --
.../components/header_page/index.test.tsx | 228 ++++
.../public/components/header_page/index.tsx | 141 ++-
.../__snapshots__/index.test.tsx.snap | 4 +-
.../index.test.tsx | 88 +-
.../index.tsx | 40 +-
.../__snapshots__/index.test.tsx.snap | 14 +
.../components/link_icon/index.test.tsx | 121 ++
.../public/components/link_icon/index.tsx | 61 +
.../siem/public/components/link_to/index.ts | 4 +
.../public/components/link_to/link_to.tsx | 52 +-
.../link_to/redirect_to_detection_engine.tsx | 51 +
.../matrix_over_time/index.test.tsx | 4 +-
.../components/matrix_over_time/index.tsx | 4 +-
.../ml/tables/anomalies_host_table.tsx | 4 +-
.../ml/tables/anomalies_network_table.tsx | 4 +-
.../components/ml_popover/ml_popover.tsx | 37 +-
.../components/navigation/index.test.tsx | 33 +-
.../public/components/navigation/index.tsx | 7 +-
.../navigation/tab_navigation/index.test.tsx | 12 +-
.../navigation/tab_navigation/index.tsx | 68 +-
.../public/components/navigation/types.ts | 1 -
.../components/open_timeline/index.test.tsx | 2 +-
.../open_timeline/title_row/index.test.tsx | 2 +-
.../open_timeline/title_row/index.tsx | 6 +-
.../__snapshots__/index.test.tsx.snap | 7 +
.../histogram_signals/index.test.tsx | 27 +
.../histogram_signals/index.tsx | 85 ++
.../page/overview/overview_host/index.tsx | 6 +-
.../page/overview/overview_network/index.tsx | 6 +-
.../components/paginated_table/index.tsx | 6 +-
.../__snapshots__/index.test.tsx.snap | 13 +
.../components/progress_inline/index.test.tsx | 29 +
.../components/progress_inline/index.tsx | 51 +
.../__snapshots__/index.test.tsx.snap | 9 +
.../public/components/subtitle/index.test.tsx | 77 ++
.../siem/public/components/subtitle/index.tsx | 60 +
.../public/components/url_state/constants.ts | 5 +-
.../public/components/url_state/helpers.ts | 16 +-
.../siem/public/components/url_state/types.ts | 8 +-
.../__snapshots__/index.test.tsx.snap | 47 +
.../components/wrapper_page/index.test.tsx | 67 +
.../public/components/wrapper_page/index.tsx | 61 +
.../plugins/siem/public/lib/helpers/index.tsx | 6 +
.../legacy/plugins/siem/public/pages/404.tsx | 7 +-
.../detection_engine/create_rule/index.tsx | 29 +
.../create_rule/translations.ts | 11 +
.../detection_engine/detection_engine.tsx | 205 ++++
.../detection_engine_empty_page.tsx | 29 +
.../detection_engine/edit_rule/index.tsx | 128 ++
.../edit_rule/translations.ts | 11 +
.../public/pages/detection_engine/index.tsx | 45 +
.../detection_engine/rule_details/index.tsx | 660 ++++++++++
.../rule_details/translations.ts | 11 +
.../pages/detection_engine/rules/index.tsx | 1076 +++++++++++++++++
.../detection_engine/rules/translations.ts | 11 +
.../pages/detection_engine/translations.ts | 45 +
.../public/pages/home/home_navigations.tsx | 13 +-
.../plugins/siem/public/pages/home/index.tsx | 169 +--
.../siem/public/pages/home/translations.ts | 4 +
.../plugins/siem/public/pages/home/types.ts | 2 +
.../siem/public/pages/hosts/details/index.tsx | 77 +-
.../plugins/siem/public/pages/hosts/hosts.tsx | 127 +-
.../pages/network/ip_details/index.test.tsx | 2 +-
.../public/pages/network/ip_details/index.tsx | 327 ++---
.../siem/public/pages/network/network.tsx | 144 +--
.../siem/public/pages/overview/overview.tsx | 84 +-
.../public/pages/timelines/timelines_page.tsx | 26 +-
.../translations/translations/ja-JP.json | 1 -
.../translations/translations/zh-CN.json | 1 -
98 files changed, 4904 insertions(+), 843 deletions(-)
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/index.ts
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/styles.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/header_global/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/header_global/translations.ts
delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/header_page.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap
delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/header_page.test.tsx
delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/header_page.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx
rename x-pack/legacy/plugins/siem/public/components/{header_panel => header_section}/__snapshots__/index.test.tsx.snap (62%)
rename x-pack/legacy/plugins/siem/public/components/{header_panel => header_section}/index.test.tsx (52%)
rename x-pack/legacy/plugins/siem/public/components/{header_panel => header_section}/index.tsx (64%)
create mode 100644 x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/link_icon/index.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/link_icon/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/__snapshots__/index.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/progress_inline/__snapshots__/index.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/progress_inline/index.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/progress_inline/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/subtitle/__snapshots__/index.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/subtitle/index.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/wrapper_page/__snapshots__/index.test.tsx.snap
create mode 100644 x-pack/legacy/plugins/siem/public/components/wrapper_page/index.test.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/components/wrapper_page/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/translations.ts
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/translations.ts
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/translations.ts
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts
create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts
index 08bf4cebedc9c..0d5f40ae53966 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts
@@ -5,16 +5,16 @@
*/
/** Top-level (global) navigation link to the `Hosts` page */
-export const NAVIGATION_HOSTS = '[data-test-subj="navigation-link-hosts"]';
+export const NAVIGATION_HOSTS = '[data-test-subj="navigation-hosts"]';
/** Top-level (global) navigation link to the `Network` page */
-export const NAVIGATION_NETWORK = '[data-test-subj="navigation-link-network"]';
+export const NAVIGATION_NETWORK = '[data-test-subj="navigation-network"]';
/** Top-level (global) navigation link to the `Overview` page */
-export const NAVIGATION_OVERVIEW = '[data-test-subj="navigation-link-overview"]';
+export const NAVIGATION_OVERVIEW = '[data-test-subj="navigation-overview"]';
/** Top-level (global) navigation link to the `Timelines` page */
-export const NAVIGATION_TIMELINES = '[data-test-subj="navigation-link-timelines"]';
+export const NAVIGATION_TIMELINES = '[data-test-subj="navigation-timelines"]';
export const HOSTS_PAGE_TABS = {
allHosts: '[data-test-subj="navigation-allHosts"]',
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar.test.tsx.snap
new file mode 100644
index 0000000000000..1f892acef7ef3
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar.test.tsx.snap
@@ -0,0 +1,36 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`UtilityBar it renders 1`] = `
+
+
+
+
+
+ Test text
+
+
+
+
+ Test popover
+
+ }
+ >
+ Test action
+
+
+
+
+
+
+ Test action
+
+
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap
new file mode 100644
index 0000000000000..470b40cd1d960
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`UtilityBarAction it renders 1`] = `
+
+
+ Test action
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap
new file mode 100644
index 0000000000000..62ff1b17dd55f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`UtilityBarGroup it renders 1`] = `
+
+
+
+ Test text
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap
new file mode 100644
index 0000000000000..f81717c892755
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`UtilityBarSection it renders 1`] = `
+
+
+
+
+ Test text
+
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap
new file mode 100644
index 0000000000000..446b5556945d8
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`UtilityBarText it renders 1`] = `
+
+
+ Test text
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/index.ts b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/index.ts
new file mode 100644
index 0000000000000..b07fe8bb847c7
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/index.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { UtilityBar } from './utility_bar';
+export { UtilityBarAction } from './utility_bar_action';
+export { UtilityBarGroup } from './utility_bar_group';
+export { UtilityBarSection } from './utility_bar_section';
+export { UtilityBarText } from './utility_bar_text';
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/styles.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/styles.tsx
new file mode 100644
index 0000000000000..9d746bf3b0515
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/styles.tsx
@@ -0,0 +1,118 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import styled, { css } from 'styled-components';
+
+/**
+ * UTILITY BAR
+ */
+
+export interface BarProps {
+ border?: boolean;
+}
+
+export const Bar = styled.aside.attrs({
+ className: 'siemUtilityBar',
+})`
+ ${({ border, theme }) => css`
+ ${border &&
+ css`
+ border-bottom: ${theme.eui.euiBorderThin};
+ padding-bottom: ${theme.eui.paddingSizes.s};
+ `}
+
+ @media only screen and (min-width: ${theme.eui.euiBreakpoints.l}) {
+ display: flex;
+ justify-content: space-between;
+ }
+ `}
+`;
+Bar.displayName = 'Bar';
+
+export const BarSection = styled.div.attrs({
+ className: 'siemUtilityBar__section',
+})`
+ ${({ theme }) => css`
+ & + & {
+ margin-top: ${theme.eui.euiSizeS};
+ }
+
+ @media only screen and (min-width: ${theme.eui.euiBreakpoints.m}) {
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+ @media only screen and (min-width: ${theme.eui.euiBreakpoints.l}) {
+ & + & {
+ margin-top: 0;
+ margin-left: ${theme.eui.euiSize};
+ }
+ }
+ `}
+`;
+BarSection.displayName = 'BarSection';
+
+export const BarGroup = styled.div.attrs({
+ className: 'siemUtilityBar__group',
+})`
+ ${({ theme }) => css`
+ align-items: flex-start;
+ display: flex;
+ flex-wrap: wrap;
+
+ & + & {
+ margin-top: ${theme.eui.euiSizeS};
+ }
+
+ @media only screen and (min-width: ${theme.eui.euiBreakpoints.m}) {
+ border-right: ${theme.eui.euiBorderThin};
+ flex-wrap: nowrap;
+ margin-right: ${theme.eui.paddingSizes.m};
+ padding-right: ${theme.eui.paddingSizes.m};
+
+ & + & {
+ margin-top: 0;
+ }
+
+ &:last-child {
+ border-right: none;
+ margin-right: 0;
+ padding-right: 0;
+ }
+ }
+
+ & > * {
+ margin-right: ${theme.eui.euiSize};
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+ `}
+`;
+BarGroup.displayName = 'BarGroup';
+
+export const BarText = styled.p.attrs({
+ className: 'siemUtilityBar__text',
+})`
+ ${({ theme }) => css`
+ color: ${theme.eui.textColors.subdued};
+ font-size: ${theme.eui.euiFontSizeXS};
+ line-height: ${theme.eui.euiLineHeight};
+ white-space: nowrap;
+ `}
+`;
+BarText.displayName = 'BarText';
+
+export const BarAction = styled.div.attrs({
+ className: 'siemUtilityBar__action',
+})`
+ ${({ theme }) => css`
+ font-size: ${theme.eui.euiFontSizeXS};
+ line-height: ${theme.eui.euiLineHeight};
+ `}
+`;
+BarAction.displayName = 'BarAction';
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.test.tsx
new file mode 100644
index 0000000000000..bf13a503838cf
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.test.tsx
@@ -0,0 +1,113 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import { mount, shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import 'jest-styled-components';
+import React from 'react';
+
+import '../../../mock/ui_settings';
+import { TestProviders } from '../../../mock';
+import {
+ UtilityBar,
+ UtilityBarAction,
+ UtilityBarGroup,
+ UtilityBarSection,
+ UtilityBarText,
+} from './index';
+
+jest.mock('../../../lib/settings/use_kibana_ui_setting');
+
+describe('UtilityBar', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+
+
+ {'Test text'}
+
+
+
+ {'Test popover'}}>
+ {'Test action'}
+
+
+
+
+
+
+ {'Test action'}
+
+
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+
+ test('it applies border styles when border is true', () => {
+ const wrapper = mount(
+
+
+
+
+ {'Test text'}
+
+
+
+ {'Test popover'}}>
+ {'Test action'}
+
+
+
+
+
+
+ {'Test action'}
+
+
+
+
+ );
+ const siemUtilityBar = wrapper.find('.siemUtilityBar').first();
+
+ expect(siemUtilityBar).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
+ expect(siemUtilityBar).toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.s);
+ });
+
+ test('it DOES NOT apply border styles when border is false', () => {
+ const wrapper = mount(
+
+
+
+
+ {'Test text'}
+
+
+
+ {'Test popover'}}>
+ {'Test action'}
+
+
+
+
+
+
+ {'Test action'}
+
+
+
+
+ );
+ const siemUtilityBar = wrapper.find('.siemUtilityBar').first();
+
+ expect(siemUtilityBar).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
+ expect(siemUtilityBar).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.s);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.tsx
new file mode 100644
index 0000000000000..f226e0e055391
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+import { Bar, BarProps } from './styles';
+
+export interface UtilityBarProps extends BarProps {
+ children: React.ReactNode;
+}
+
+export const UtilityBar = React.memo(({ border, children }) => (
+ {children}
+));
+UtilityBar.displayName = 'UtilityBar';
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.test.tsx
new file mode 100644
index 0000000000000..7a1c35183e503
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.test.tsx
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { mount, shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import 'jest-styled-components';
+import React from 'react';
+
+import '../../../mock/ui_settings';
+import { TestProviders } from '../../../mock';
+import { UtilityBarAction } from './index';
+
+jest.mock('../../../lib/settings/use_kibana_ui_setting');
+
+describe('UtilityBarAction', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+ {'Test action'}
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+
+ test('it renders a popover', () => {
+ const wrapper = mount(
+
+ {'Test popover'}}>
+ {'Test action'}
+
+
+ );
+
+ expect(
+ wrapper
+ .find('.euiPopover')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.tsx
new file mode 100644
index 0000000000000..ae4362bdbcd7b
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.tsx
@@ -0,0 +1,72 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiPopover } from '@elastic/eui';
+import React, { useState } from 'react';
+
+import { LinkIcon, LinkIconProps } from '../../link_icon';
+import { BarAction } from './styles';
+
+const Popover = React.memo(
+ ({ children, color, iconSide, iconSize, iconType, popoverContent }) => {
+ const [popoverState, setPopoverState] = useState(false);
+
+ return (
+ setPopoverState(!popoverState)}
+ >
+ {children}
+
+ }
+ closePopover={() => setPopoverState(false)}
+ isOpen={popoverState}
+ >
+ {popoverContent}
+
+ );
+ }
+);
+Popover.displayName = 'Popover';
+
+export interface UtilityBarActionProps extends LinkIconProps {
+ popoverContent?: React.ReactNode;
+}
+
+export const UtilityBarAction = React.memo(
+ ({ children, color, href, iconSide, iconSize, iconType, onClick, popoverContent }) => (
+
+ {popoverContent ? (
+
+ {children}
+
+ ) : (
+
+ {children}
+
+ )}
+
+ )
+);
+UtilityBarAction.displayName = 'UtilityBarAction';
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.test.tsx
new file mode 100644
index 0000000000000..84ad96c5a1e5e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.test.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import React from 'react';
+
+import '../../../mock/ui_settings';
+import { TestProviders } from '../../../mock';
+import { UtilityBarGroup, UtilityBarText } from './index';
+
+jest.mock('../../../lib/settings/use_kibana_ui_setting');
+
+describe('UtilityBarGroup', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+ {'Test text'}
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.tsx
new file mode 100644
index 0000000000000..1e23fd3498199
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+import { BarGroup } from './styles';
+
+export interface UtilityBarGroupProps {
+ children: React.ReactNode;
+}
+
+export const UtilityBarGroup = React.memo(({ children }) => (
+ {children}
+));
+UtilityBarGroup.displayName = 'UtilityBarGroup';
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.test.tsx
new file mode 100644
index 0000000000000..2dfc1d3b8d193
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.test.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import React from 'react';
+
+import '../../../mock/ui_settings';
+import { TestProviders } from '../../../mock';
+import { UtilityBarGroup, UtilityBarSection, UtilityBarText } from './index';
+
+jest.mock('../../../lib/settings/use_kibana_ui_setting');
+
+describe('UtilityBarSection', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+
+ {'Test text'}
+
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.tsx
new file mode 100644
index 0000000000000..c457e6bc3dee0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+import { BarSection } from './styles';
+
+export interface UtilityBarSectionProps {
+ children: React.ReactNode;
+}
+
+export const UtilityBarSection = React.memo(({ children }) => (
+ {children}
+));
+UtilityBarSection.displayName = 'UtilityBarSection';
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.test.tsx
new file mode 100644
index 0000000000000..0743e5cab02b4
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import React from 'react';
+
+import '../../../mock/ui_settings';
+import { TestProviders } from '../../../mock';
+import { UtilityBarText } from './index';
+
+jest.mock('../../../lib/settings/use_kibana_ui_setting');
+
+describe('UtilityBarText', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+ {'Test text'}
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.tsx
new file mode 100644
index 0000000000000..f8eb25f03d4ad
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+import { BarText } from './styles';
+
+export interface UtilityBarTextProps {
+ children: string;
+}
+
+export const UtilityBarText = React.memo(({ children }) => (
+ {children}
+));
+UtilityBarText.displayName = 'UtilityBarText';
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx
index a97ef2cf5ca0c..4e59acc4f6713 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx
@@ -52,7 +52,7 @@ describe('EventsViewer', () => {
expect(
wrapper
- .find(`[data-test-subj="header-panel-subtitle"]`)
+ .find(`[data-test-subj="header-section-subtitle"]`)
.first()
.text()
).toEqual('Showing: 12 events');
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
index 2b588283bd198..c5c0fe3503561 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
@@ -16,7 +16,7 @@ import { Direction } from '../../graphql/types';
import { useKibanaCore } from '../../lib/compose/kibana_core';
import { KqlMode } from '../../store/timeline/model';
import { AutoSizer } from '../auto_sizer';
-import { HeaderPanel } from '../header_panel';
+import { HeaderSection } from '../header_section';
import { ColumnHeader } from '../timeline/body/column_headers/column_header';
import { defaultHeaders } from '../timeline/body/column_headers/default_headers';
import { Sort } from '../timeline/body/sort';
@@ -130,7 +130,7 @@ export const EventsViewer = React.memo(
totalCount = 0,
}) => (
<>
- `
+const Wrapper = styled.aside<{ isSticky?: boolean }>`
${props => css`
position: relative;
z-index: ${props.theme.eui.euiZNavigation};
background: ${props.theme.eui.euiColorEmptyShade};
border-bottom: ${props.theme.eui.euiBorderThin};
- box-sizing: content-box;
- margin: 0 -${gutterTimeline} 0 -${props.theme.eui.euiSizeL};
- padding: ${props.theme.eui.euiSize} ${gutterTimeline} ${props.theme.eui.euiSize} ${
- props.theme.eui.euiSizeL
- };
+ padding: ${props.theme.eui.paddingSizes.m} ${gutterTimeline} ${
+ props.theme.eui.paddingSizes.m
+ } ${props.theme.eui.paddingSizes.l};
${props.isSticky &&
`
@@ -39,8 +38,7 @@ const Aside = styled.aside<{ isSticky?: boolean }>`
}
`}
`;
-
-Aside.displayName = 'Aside';
+Wrapper.displayName = 'Wrapper';
export interface FiltersGlobalProps {
children: React.ReactNode;
@@ -49,11 +47,10 @@ export interface FiltersGlobalProps {
export const FiltersGlobal = pure(({ children }) => (
{({ style, isSticky }) => (
-
+
)}
));
-
FiltersGlobal.displayName = 'FiltersGlobal';
diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap
new file mode 100644
index 0000000000000..665a5c75f3684
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,7 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HeaderGlobal it renders 1`] = `
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx
new file mode 100644
index 0000000000000..ebd1da634ed1a
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import React from 'react';
+
+import { TestProviders } from '../../mock';
+import '../../mock/match_media';
+import '../../mock/ui_settings';
+import { HeaderGlobal } from './index';
+
+jest.mock('../../lib/settings/use_kibana_ui_setting');
+
+// Test will fail because we will to need to mock some core services to make the test work
+// For now let's forget about SiemSearchBar
+jest.mock('../search_bar', () => ({
+ SiemSearchBar: () => null,
+}));
+
+describe('HeaderGlobal', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx
new file mode 100644
index 0000000000000..168cacf3e97e1
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx
@@ -0,0 +1,82 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink } from '@elastic/eui';
+import { pickBy } from 'lodash/fp';
+import React from 'react';
+import styled, { css } from 'styled-components';
+
+import { gutterTimeline } from '../../lib/helpers';
+import { navTabs } from '../../pages/home/home_navigations';
+import { SiemPageName } from '../../pages/home/types';
+import { getOverviewUrl } from '../link_to';
+import { MlPopover } from '../ml_popover/ml_popover';
+import { SiemNavigation } from '../navigation';
+import * as i18n from './translations';
+
+const Wrapper = styled.header`
+ ${({ theme }) => css`
+ background: ${theme.eui.euiColorEmptyShade};
+ border-bottom: ${theme.eui.euiBorderThin};
+ padding: ${theme.eui.paddingSizes.m} ${gutterTimeline} ${theme.eui.paddingSizes.m}
+ ${theme.eui.paddingSizes.l};
+ `}
+`;
+Wrapper.displayName = 'Wrapper';
+
+const FlexItem = styled(EuiFlexItem)`
+ min-width: 0;
+`;
+FlexItem.displayName = 'FlexItem';
+
+interface HeaderGlobalProps {
+ hideDetectionEngine?: boolean;
+}
+export const HeaderGlobal = React.memo(({ hideDetectionEngine = true }) => (
+
+
+
+
+
+
+
+
+
+
+
+ key !== SiemPageName.detectionEngine, navTabs)
+ : navTabs
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ {i18n.BUTTON_ADD_DATA}
+
+
+
+
+
+
+));
+HeaderGlobal.displayName = 'HeaderGlobal';
diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/translations.ts b/x-pack/legacy/plugins/siem/public/components/header_global/translations.ts
new file mode 100644
index 0000000000000..c713f63016594
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_global/translations.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const SIEM = i18n.translate('xpack.siem.headerGlobal.siem', {
+ defaultMessage: 'SIEM',
+});
+
+export const BUTTON_ADD_DATA = i18n.translate('xpack.siem.headerGlobal.buttonAddData', {
+ defaultMessage: 'Add data',
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/header_page.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/header_page.test.tsx.snap
deleted file mode 100644
index 280acc0c63334..0000000000000
--- a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/header_page.test.tsx.snap
+++ /dev/null
@@ -1,14 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`rendering renders correctly 1`] = `
-
-
- My test supplement.
-
-
-`;
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap
new file mode 100644
index 0000000000000..0fe2890dc9f24
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,23 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HeaderPage it renders 1`] = `
+
+
+
+ Test supplement
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/header_page.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/header_page.test.tsx
deleted file mode 100644
index 16f2156e568e5..0000000000000
--- a/x-pack/legacy/plugins/siem/public/components/header_page/header_page.test.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { shallow } from 'enzyme';
-import toJson from 'enzyme-to-json';
-import React from 'react';
-
-import { HeaderPage } from './index';
-
-describe('rendering', () => {
- test('renders correctly', () => {
- const wrapper = shallow(
-
- {'My test supplement.'}
-
- );
- expect(toJson(wrapper)).toMatchSnapshot();
- });
- test('renders as a draggable when provided arguments', () => {
- const wrapper = shallow(
-
- {'My test supplement.'}
-
- );
- const draggableHeader = wrapper.dive().find('[data-test-subj="page_headline_draggable"]');
- expect(draggableHeader.exists()).toBeTruthy();
- });
-});
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/header_page.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/header_page.tsx
deleted file mode 100644
index 2ba543b34307a..0000000000000
--- a/x-pack/legacy/plugins/siem/public/components/header_page/header_page.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiText, EuiTitle } from '@elastic/eui';
-import React from 'react';
-import { pure } from 'recompose';
-import styled from 'styled-components';
-import { DefaultDraggable } from '../draggables';
-
-const Header = styled.header`
- ${({ theme }) => `
- border-bottom: ${theme.eui.euiBorderThin};
- padding-bottom: ${theme.eui.euiSizeL};
- margin: ${theme.eui.euiSizeL} 0;
- `}
-`;
-
-Header.displayName = 'Header';
-
-interface DraggableArguments {
- field: string;
- value: string;
-}
-
-export interface HeaderPageProps {
- badgeLabel?: string;
- badgeTooltip?: string;
- children?: React.ReactNode;
- draggableArguments?: DraggableArguments;
- subtitle?: string | React.ReactNode;
- title: string | React.ReactNode;
-}
-
-export const HeaderPage = pure(
- ({ badgeLabel, badgeTooltip, children, draggableArguments, subtitle, title, ...rest }) => (
-
-
-
-
-
- {!draggableArguments ? (
- title
- ) : (
-
- )}
- {badgeLabel && (
- <>
- {' '}
-
- >
- )}
-
-
-
-
- {subtitle}
-
-
-
- {children && {children}}
-
-
- )
-);
-
-HeaderPage.displayName = 'HeaderPage';
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx
new file mode 100644
index 0000000000000..ae33b63e93d39
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx
@@ -0,0 +1,228 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import { mount, shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import 'jest-styled-components';
+import React from 'react';
+
+import { TestProviders } from '../../mock';
+import '../../mock/ui_settings';
+import { HeaderPage } from './index';
+
+jest.mock('../../lib/settings/use_kibana_ui_setting');
+
+describe('HeaderPage', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+ {'Test supplement'}
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+
+ test('it renders the title', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('[data-test-subj="header-page-title"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test('it renders the back link when provided', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('.siemHeaderPage__linkBack')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test('it DOES NOT render the back link when not provided', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('.siemHeaderPage__linkBack')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test('it renders the first subtitle when provided', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('[data-test-subj="header-page-subtitle"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test('it DOES NOT render the first subtitle when not provided', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('[data-test-subj="header-section-subtitle"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test('it renders the second subtitle when provided', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('[data-test-subj="header-page-subtitle-2"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test('it DOES NOT render the second subtitle when not provided', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('[data-test-subj="header-section-subtitle-2"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test('it renders supplements when children provided', () => {
+ const wrapper = mount(
+
+
+ {'Test supplement'}
+
+
+ );
+
+ expect(
+ wrapper
+ .find('[data-test-subj="header-page-supplements"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test('it DOES NOT render supplements when children not provided', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('[data-test-subj="header-page-supplements"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test('it applies border styles when border is true', () => {
+ const wrapper = mount(
+
+
+
+ );
+ const siemHeaderPage = wrapper.find('.siemHeaderPage').first();
+
+ expect(siemHeaderPage).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
+ expect(siemHeaderPage).toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
+ });
+
+ test('it DOES NOT apply border styles when border is false', () => {
+ const wrapper = mount(
+
+
+
+ );
+ const siemHeaderPage = wrapper.find('.siemHeaderPage').first();
+
+ expect(siemHeaderPage).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
+ expect(siemHeaderPage).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
+ });
+
+ test('it renders as a draggable when arguments provided', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('[data-test-subj="header-page-draggable"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test('it DOES NOT render as a draggable when arguments not provided', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find('[data-test-subj="header-page-draggable"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx
index 9d89cdfc32893..4db2a35c600e9 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx
@@ -4,4 +4,143 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { HeaderPage } from './header_page';
+import { EuiBetaBadge, EuiBadge, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
+import React from 'react';
+import styled, { css } from 'styled-components';
+
+import { DefaultDraggable } from '../draggables';
+import { LinkIcon, LinkIconProps } from '../link_icon';
+import { Subtitle, SubtitleProps } from '../subtitle';
+
+interface HeaderProps {
+ border?: boolean;
+}
+
+const Header = styled.header.attrs({
+ className: 'siemHeaderPage',
+})`
+ ${({ border, theme }) => css`
+ margin-bottom: ${theme.eui.euiSizeL};
+
+ ${border &&
+ css`
+ border-bottom: ${theme.eui.euiBorderThin};
+ padding-bottom: ${theme.eui.paddingSizes.l};
+ `}
+ `}
+`;
+Header.displayName = 'Header';
+
+const FlexItem = styled(EuiFlexItem)`
+ display: block;
+`;
+FlexItem.displayName = 'FlexItem';
+
+const LinkBack = styled.div.attrs({
+ className: 'siemHeaderPage__linkBack',
+})`
+ ${({ theme }) => css`
+ font-size: ${theme.eui.euiFontSizeXS};
+ line-height: ${theme.eui.euiLineHeight};
+ margin-bottom: ${theme.eui.euiSizeS};
+ `}
+`;
+LinkBack.displayName = 'LinkBack';
+
+const Badge = styled(EuiBadge)`
+ letter-spacing: 0;
+`;
+Badge.displayName = 'Badge';
+
+interface BackOptions {
+ href: LinkIconProps['href'];
+ text: LinkIconProps['children'];
+}
+
+interface BadgeOptions {
+ beta?: boolean;
+ text: string;
+ tooltip?: string;
+}
+
+interface DraggableArguments {
+ field: string;
+ value: string;
+}
+
+export interface HeaderPageProps extends HeaderProps {
+ backOptions?: BackOptions;
+ badgeOptions?: BadgeOptions;
+ children?: React.ReactNode;
+ draggableArguments?: DraggableArguments;
+ subtitle?: SubtitleProps['items'];
+ subtitle2?: SubtitleProps['items'];
+ title: string | React.ReactNode;
+}
+
+export const HeaderPage = React.memo(
+ ({
+ backOptions,
+ badgeOptions,
+ border,
+ children,
+ draggableArguments,
+ subtitle,
+ subtitle2,
+ title,
+ ...rest
+ }) => (
+
+
+
+ {backOptions && (
+
+
+ {backOptions.text}
+
+
+ )}
+
+
+
+ {!draggableArguments ? (
+ title
+ ) : (
+
+ )}
+ {badgeOptions && (
+ <>
+ {' '}
+ {badgeOptions.beta ? (
+
+ ) : (
+ {badgeOptions.text}
+ )}
+ >
+ )}
+
+
+
+ {subtitle && }
+ {subtitle2 && }
+
+
+ {children && (
+
+ {children}
+
+ )}
+
+
+ )
+);
+HeaderPage.displayName = 'HeaderPage';
diff --git a/x-pack/legacy/plugins/siem/public/components/header_panel/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap
similarity index 62%
rename from x-pack/legacy/plugins/siem/public/components/header_panel/__snapshots__/index.test.tsx.snap
rename to x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap
index 39250c38ef8fc..ecd2b15a841f6 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_panel/__snapshots__/index.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap
@@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`HeaderPanel it renders 1`] = `
+exports[`HeaderSection it renders 1`] = `
-
diff --git a/x-pack/legacy/plugins/siem/public/components/header_panel/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx
similarity index 52%
rename from x-pack/legacy/plugins/siem/public/components/header_panel/index.test.tsx
rename to x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx
index 9cdb85bcb3d76..fffeece818d13 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_panel/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx
@@ -10,17 +10,17 @@ import toJson from 'enzyme-to-json';
import 'jest-styled-components';
import React from 'react';
-import '../../mock/ui_settings';
import { TestProviders } from '../../mock';
-import { HeaderPanel } from './index';
+import '../../mock/ui_settings';
+import { HeaderSection } from './index';
jest.mock('../../lib/settings/use_kibana_ui_setting');
-describe('HeaderPanel', () => {
+describe('HeaderSection', () => {
test('it renders', () => {
const wrapper = shallow(
-
+
);
@@ -30,13 +30,13 @@ describe('HeaderPanel', () => {
test('it renders the title', () => {
const wrapper = mount(
-
+
);
expect(
wrapper
- .find('[data-test-subj="header-panel-title"]')
+ .find('[data-test-subj="header-section-title"]')
.first()
.exists()
).toBe(true);
@@ -45,13 +45,13 @@ describe('HeaderPanel', () => {
test('it renders the subtitle when provided', () => {
const wrapper = mount(
-
+
);
expect(
wrapper
- .find(`[data-test-subj="header-panel-subtitle"]`)
+ .find('[data-test-subj="header-section-subtitle"]')
.first()
.exists()
).toBe(true);
@@ -60,13 +60,13 @@ describe('HeaderPanel', () => {
test('it DOES NOT render the subtitle when not provided', () => {
const wrapper = mount(
-
+
);
expect(
wrapper
- .find(`[data-test-subj="header-panel-subtitle"]`)
+ .find('[data-test-subj="header-section-subtitle"]')
.first()
.exists()
).toBe(false);
@@ -75,13 +75,13 @@ describe('HeaderPanel', () => {
test('it renders a transparent inspect button when showInspect is false', () => {
const wrapper = mount(
-
+
);
expect(
wrapper
- .find(`[data-test-subj="transparent-inspect-container"]`)
+ .find('[data-test-subj="transparent-inspect-container"]')
.first()
.exists()
).toBe(true);
@@ -90,13 +90,13 @@ describe('HeaderPanel', () => {
test('it renders an opaque inspect button when showInspect is true', () => {
const wrapper = mount(
-
+
);
expect(
wrapper
- .find(`[data-test-subj="opaque-inspect-container"]`)
+ .find('[data-test-subj="opaque-inspect-container"]')
.first()
.exists()
).toBe(true);
@@ -105,15 +105,15 @@ describe('HeaderPanel', () => {
test('it renders supplements when children provided', () => {
const wrapper = mount(
-
+
{'Test children'}
-
+
);
expect(
wrapper
- .find('[data-test-subj="header-panel-supplements"]')
+ .find('[data-test-subj="header-section-supplements"]')
.first()
.exists()
).toBe(true);
@@ -122,13 +122,13 @@ describe('HeaderPanel', () => {
test('it DOES NOT render supplements when children not provided', () => {
const wrapper = mount(
-
+
);
expect(
wrapper
- .find('[data-test-subj="header-panel-supplements"]')
+ .find('[data-test-subj="header-section-supplements"]')
.first()
.exists()
).toBe(false);
@@ -137,24 +137,58 @@ describe('HeaderPanel', () => {
test('it applies border styles when border is true', () => {
const wrapper = mount(
-
+
);
- const siemHeaderPanel = wrapper.find('.siemHeaderPanel').first();
+ const siemHeaderSection = wrapper.find('.siemHeaderSection').first();
- expect(siemHeaderPanel).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
- expect(siemHeaderPanel).toHaveStyleRule('padding-bottom', euiDarkVars.euiSizeL);
+ expect(siemHeaderSection).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
+ expect(siemHeaderSection).toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
});
test('it DOES NOT apply border styles when border is false', () => {
const wrapper = mount(
-
+
+
+ );
+ const siemHeaderSection = wrapper.find('.siemHeaderSection').first();
+
+ expect(siemHeaderSection).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
+ expect(siemHeaderSection).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
+ });
+
+ test('it splits the title and supplement areas evenly when split is true', () => {
+ const wrapper = mount(
+
+
+ {'Test children'}
+
+
+ );
+
+ expect(
+ wrapper
+ .find('.euiFlexItem--flexGrowZero[data-test-subj="header-section-supplements"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test('it DOES NOT split the title and supplement areas evenly when split is false', () => {
+ const wrapper = mount(
+
+
+ {'Test children'}
+
);
- const siemHeaderPanel = wrapper.find('.siemHeaderPanel').first();
- expect(siemHeaderPanel).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
- expect(siemHeaderPanel).not.toHaveStyleRule('padding-bottom', euiDarkVars.euiSizeL);
+ expect(
+ wrapper
+ .find('.euiFlexItem--flexGrowZero[data-test-subj="header-section-supplements"]')
+ .first()
+ .exists()
+ ).toBe(true);
});
});
diff --git a/x-pack/legacy/plugins/siem/public/components/header_panel/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_section/index.tsx
similarity index 64%
rename from x-pack/legacy/plugins/siem/public/components/header_panel/index.tsx
rename to x-pack/legacy/plugins/siem/public/components/header_section/index.tsx
index e7b3fb9f2f400..e46ae55a57a45 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_panel/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/header_section/index.tsx
@@ -4,51 +4,52 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiText, EuiTitle } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiTitle } from '@elastic/eui';
import React from 'react';
import styled, { css } from 'styled-components';
import { InspectButton } from '../inspect';
+import { Subtitle } from '../subtitle';
interface HeaderProps {
border?: boolean;
}
const Header = styled.header.attrs({
- className: 'siemHeaderPanel',
+ className: 'siemHeaderSection',
})`
- ${props => css`
- margin-bottom: ${props.theme.eui.euiSizeL};
+ ${({ border, theme }) => css`
+ margin-bottom: ${theme.eui.euiSizeL};
user-select: text;
- ${props.border &&
- `
- border-bottom: ${props.theme.eui.euiBorderThin};
- padding-bottom: ${props.theme.eui.euiSizeL};
- `}
+ ${border &&
+ css`
+ border-bottom: ${theme.eui.euiBorderThin};
+ padding-bottom: ${theme.eui.paddingSizes.l};
+ `}
`}
`;
-
Header.displayName = 'Header';
-export interface HeaderPanelProps extends HeaderProps {
+export interface HeaderSectionProps extends HeaderProps {
children?: React.ReactNode;
id?: string;
+ split?: boolean;
subtitle?: string | React.ReactNode;
showInspect?: boolean;
title: string | React.ReactNode;
tooltip?: string;
}
-export const HeaderPanel = React.memo(
- ({ border, children, id, showInspect = false, subtitle, title, tooltip }) => (
+export const HeaderSection = React.memo(
+ ({ border, children, id, showInspect = false, split, subtitle, title, tooltip }) => (
-
+
{title}
{tooltip && (
<>
@@ -59,11 +60,7 @@ export const HeaderPanel = React.memo(
- {subtitle && (
-
- {subtitle}
-
- )}
+ {subtitle && }
{id && (
@@ -75,7 +72,7 @@ export const HeaderPanel = React.memo(
{children && (
-
+
{children}
)}
@@ -83,5 +80,4 @@ export const HeaderPanel = React.memo(
)
);
-
-HeaderPanel.displayName = 'HeaderPanel';
+HeaderSection.displayName = 'HeaderSection';
diff --git a/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap
new file mode 100644
index 0000000000000..5902768383cb0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,14 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`LinkIcon it renders 1`] = `
+
+
+ Test link
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/link_icon/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/link_icon/index.test.tsx
new file mode 100644
index 0000000000000..8e4387f35056e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/link_icon/index.test.tsx
@@ -0,0 +1,121 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { mount, shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import 'jest-styled-components';
+import React from 'react';
+
+import { TestProviders } from '../../mock';
+import '../../mock/ui_settings';
+import { LinkIcon } from './index';
+
+jest.mock('../../lib/settings/use_kibana_ui_setting');
+
+describe('LinkIcon', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+ {'Test link'}
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+
+ test('it renders an action button when onClick is provided', () => {
+ const wrapper = mount(
+
+ alert('Test alert')}>
+ {'Test link'}
+
+
+ );
+
+ expect(
+ wrapper
+ .find('button')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test('it renders an action link when href is provided', () => {
+ const wrapper = mount(
+
+
+ {'Test link'}
+
+
+ );
+
+ expect(
+ wrapper
+ .find('a')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test('it renders an icon', () => {
+ const wrapper = mount(
+
+ {'Test link'}
+
+ );
+
+ expect(
+ wrapper
+ .find('.euiIcon')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test('it positions the icon to the right when iconSide is right', () => {
+ const wrapper = mount(
+
+
+ {'Test link'}
+
+
+ );
+
+ expect(wrapper.find('.siemLinkIcon').first()).toHaveStyleRule('flex-direction', 'row-reverse');
+ });
+
+ test('it positions the icon to the left when iconSide is left (or not provided)', () => {
+ const wrapper = mount(
+
+
+ {'Test link'}
+
+
+ );
+
+ expect(wrapper.find('.siemLinkIcon').first()).not.toHaveStyleRule(
+ 'flex-direction',
+ 'row-reverse'
+ );
+ });
+
+ test('it renders a label', () => {
+ const wrapper = mount(
+
+ {'Test link'}
+
+ );
+
+ expect(
+ wrapper
+ .find('.siemLinkIcon__label')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/link_icon/index.tsx b/x-pack/legacy/plugins/siem/public/components/link_icon/index.tsx
new file mode 100644
index 0000000000000..d83183adcf5e5
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/link_icon/index.tsx
@@ -0,0 +1,61 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiIcon, EuiLink, IconSize, IconType } from '@elastic/eui';
+import { LinkAnchorProps } from '@elastic/eui/src/components/link/link';
+import React from 'react';
+import styled, { css } from 'styled-components';
+
+interface LinkProps {
+ color?: LinkAnchorProps['color'];
+ href?: string;
+ iconSide?: 'left' | 'right';
+ onClick?: Function;
+}
+
+const Link = styled(({ iconSide, children, ...rest }) => {children})<
+ LinkProps
+>`
+ ${({ iconSide, theme }) => css`
+ align-items: center;
+ display: inline-flex;
+ vertical-align: top;
+ white-space: nowrap;
+
+ ${iconSide === 'left' &&
+ css`
+ .euiIcon {
+ margin-right: ${theme.eui.euiSizeXS};
+ }
+ `}
+
+ ${iconSide === 'right' &&
+ css`
+ flex-direction: row-reverse;
+
+ .euiIcon {
+ margin-left: ${theme.eui.euiSizeXS};
+ }
+ `}
+ `}
+`;
+Link.displayName = 'Link';
+
+export interface LinkIconProps extends LinkProps {
+ children: string;
+ iconSize?: IconSize;
+ iconType: IconType;
+}
+
+export const LinkIcon = React.memo(
+ ({ children, color, href, iconSide = 'left', iconSize = 's', iconType, onClick }) => (
+
+
+ {children}
+
+ )
+);
+LinkIcon.displayName = 'LinkIcon';
diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/index.ts b/x-pack/legacy/plugins/siem/public/components/link_to/index.ts
index 7eb39de3d96b4..10198345755c3 100644
--- a/x-pack/legacy/plugins/siem/public/components/link_to/index.ts
+++ b/x-pack/legacy/plugins/siem/public/components/link_to/index.ts
@@ -5,6 +5,10 @@
*/
export { LinkToPage } from './link_to';
+export {
+ getDetectionEngineUrl,
+ RedirectToDetectionEnginePage,
+} from './redirect_to_detection_engine';
export { getOverviewUrl, RedirectToOverviewPage } from './redirect_to_overview';
export { getHostsUrl, getHostDetailsUrl } from './redirect_to_hosts';
export { getNetworkUrl, getIPDetailsUrl, RedirectToNetworkPage } from './redirect_to_network';
diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx
index 0360c1004f151..0125b52e3ad33 100644
--- a/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx
@@ -8,12 +8,19 @@ import React from 'react';
import { match as RouteMatch, Redirect, Route, Switch } from 'react-router-dom';
import { pure } from 'recompose';
+import { SiemPageName } from '../../pages/home/types';
+import { HostsTableType } from '../../store/hosts/model';
+import {
+ RedirectToCreateRulePage,
+ RedirectToDetectionEnginePage,
+ RedirectToEditRulePage,
+ RedirectToRuleDetailsPage,
+ RedirectToRulesPage,
+} from './redirect_to_detection_engine';
import { RedirectToHostsPage, RedirectToHostDetailsPage } from './redirect_to_hosts';
import { RedirectToNetworkPage } from './redirect_to_network';
import { RedirectToOverviewPage } from './redirect_to_overview';
import { RedirectToTimelinesPage } from './redirect_to_timelines';
-import { HostsTableType } from '../../store/hosts/model';
-import { SiemPageName } from '../../pages/home/types';
interface LinkToPageProps {
match: RouteMatch<{}>;
@@ -22,39 +29,62 @@ interface LinkToPageProps {
export const LinkToPage = pure(({ match }) => (
-
+
+
+
+
+
diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx
new file mode 100644
index 0000000000000..74aec076ec4d5
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx
@@ -0,0 +1,51 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { RouteComponentProps } from 'react-router-dom';
+
+import { RedirectWrapper } from './redirect_wrapper';
+
+export type DetectionEngineComponentProps = RouteComponentProps<{
+ search: string;
+}>;
+
+export const DETECTION_ENGINE_PAGE_NAME = 'detection-engine';
+
+export const RedirectToDetectionEnginePage = ({
+ location: { search },
+}: DetectionEngineComponentProps) => (
+
+);
+
+export const RedirectToRulesPage = ({ location: { search } }: DetectionEngineComponentProps) => {
+ return ;
+};
+
+export const RedirectToCreateRulePage = ({
+ location: { search },
+}: DetectionEngineComponentProps) => {
+ return ;
+};
+
+export const RedirectToRuleDetailsPage = ({
+ location: { search },
+}: DetectionEngineComponentProps) => {
+ return ;
+};
+
+export const RedirectToEditRulePage = ({ location: { search } }: DetectionEngineComponentProps) => {
+ return (
+
+ );
+};
+
+export const getDetectionEngineUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}`;
+export const getRulesUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules`;
+export const getCreateRuleUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/create-rule`;
+export const getRuleDetailsUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/rule-details`;
+export const getEditRuleUrl = () =>
+ `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/rule-details/edit-rule`;
diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx
index 3334447739fc5..9d2ef203361bf 100644
--- a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx
@@ -26,9 +26,9 @@ jest.mock('../../lib/settings/use_kibana_ui_setting', () => {
return { useKibanaUiSetting: () => [false] };
});
-jest.mock('../header_panel', () => {
+jest.mock('../header_section', () => {
return {
- HeaderPanel: () => ,
+ HeaderSection: () => ,
};
});
diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx
index 3523723574be6..75e1531ea2b5b 100644
--- a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx
@@ -12,7 +12,7 @@ import darkTheme from '@elastic/eui/dist/eui_theme_dark.json';
import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
import { EuiLoadingContent } from '@elastic/eui';
import { BarChart } from '../charts/barchart';
-import { HeaderPanel } from '../header_panel';
+import { HeaderSection } from '../header_section';
import { ChartSeriesData, UpdateDateRange } from '../charts/common';
import { MatrixOverTimeHistogramData } from '../../graphql/types';
import { DEFAULT_DARK_MODE } from '../../../common/constants';
@@ -113,7 +113,7 @@ export const MatrixOverTimeHistogram = ({
onMouseEnter={() => setShowInspect(true)}
onMouseLeave={() => setShowInspect(false)}
>
- (
} else {
return (
- (
} else {
return (
- {
anchorPosition="downRight"
id="integrations-popover"
button={
- setIsPopoverOpen(!isPopoverOpen)}
>
{i18n.ANOMALY_DETECTION}
-
+
}
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(!isPopoverOpen)}
@@ -183,7 +184,7 @@ export const MlPopover = React.memo(() => {
anchorPosition="downRight"
id="integrations-popover"
button={
- {
}}
>
{i18n.ANOMALY_DETECTION}
-
+
}
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(!isPopoverOpen)}
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx
index cf519da617183..97cf9522f488f 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx
@@ -61,6 +61,13 @@ describe('SIEM Navigation', () => {
expect(setBreadcrumbs).toHaveBeenNthCalledWith(1, {
detailName: undefined,
navTabs: {
+ 'detection-engine': {
+ disabled: false,
+ href: '#/link-to/detection-engine',
+ id: 'detection-engine',
+ name: 'Detection engine',
+ urlKey: 'detection-engine',
+ },
hosts: {
disabled: false,
href: '#/link-to/hosts',
@@ -132,9 +139,17 @@ describe('SIEM Navigation', () => {
tabName: undefined,
});
wrapper.update();
- expect(setBreadcrumbs).toHaveBeenNthCalledWith(2, {
+ expect(setBreadcrumbs).toHaveBeenNthCalledWith(1, {
detailName: undefined,
+ filters: [],
navTabs: {
+ 'detection-engine': {
+ disabled: false,
+ href: '#/link-to/detection-engine',
+ id: 'detection-engine',
+ name: 'Detection engine',
+ urlKey: 'detection-engine',
+ },
hosts: {
disabled: false,
href: '#/link-to/hosts',
@@ -164,17 +179,13 @@ describe('SIEM Navigation', () => {
urlKey: 'timeline',
},
},
- pageName: 'network',
- pathName: '/network',
- search: '',
- tabName: undefined,
- query: { query: '', language: 'kuery' },
- filters: [],
+ pageName: 'hosts',
+ pathName: '/hosts',
+ query: { language: 'kuery', query: '' },
savedQuery: undefined,
- timeline: {
- id: '',
- isOpen: false,
- },
+ search: '',
+ tabName: 'authentications',
+ timeline: { id: '', isOpen: false },
timerange: {
global: {
linkTo: ['timeline'],
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx
index ae8d09eeff112..7209be4d715f3 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx
@@ -6,17 +6,16 @@
import { isEqual } from 'lodash/fp';
import React, { useEffect } from 'react';
-import { compose } from 'redux';
import { connect } from 'react-redux';
+import { compose } from 'redux';
import { RouteSpyState } from '../../utils/route/types';
import { useRouteSpy } from '../../utils/route/use_route_spy';
-
+import { makeMapStateToProps } from '../url_state/helpers';
import { setBreadcrumbs } from './breadcrumbs';
import { TabNavigation } from './tab_navigation';
import { TabNavigationProps } from './tab_navigation/types';
import { SiemNavigationComponentProps } from './types';
-import { makeMapStateToProps } from '../url_state/helpers';
export const SiemNavigationComponent = React.memo(
({
@@ -29,7 +28,6 @@ export const SiemNavigationComponent = React.memo {
const pageName = SiemPageName.hosts;
@@ -78,7 +78,7 @@ describe('Tab Navigation', () => {
});
test('it carries the url state in the link', () => {
const wrapper = shallow();
- const firstTab = wrapper.find('[data-test-subj="navigation-link-network"]');
+ const firstTab = wrapper.find('[data-test-subj="navigation-network"]');
expect(firstTab.props().href).toBe(
"#/link-to/network?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))"
);
@@ -147,7 +147,7 @@ describe('Tab Navigation', () => {
test('it carries the url state in the link', () => {
const wrapper = shallow();
const firstTab = wrapper.find(
- `[data-test-subj="navigation-link-${HostsTableType.authentications}"]`
+ `[data-test-subj="navigation-${HostsTableType.authentications}"]`
);
expect(firstTab.props().href).toBe(
`#/${pageName}/${hostName}/${HostsTableType.authentications}?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx
index 3e3c02a1abfa4..27d10cb02a856 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx
@@ -3,40 +3,17 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiTab, EuiTabs, EuiLink } from '@elastic/eui';
+import { EuiTab, EuiTabs } from '@elastic/eui';
import { getOr } from 'lodash/fp';
-
import React, { useEffect, useState } from 'react';
-import styled from 'styled-components';
-import classnames from 'classnames';
import { trackUiAction as track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/track_usage';
import { getSearch } from '../helpers';
import { TabNavigationProps } from './types';
-const TabContainer = styled.div`
- .euiLink {
- color: inherit !important;
-
- &:focus {
- outline: 0;
- background: none;
- }
-
- .euiTab.euiTab-isSelected {
- cursor: pointer;
- }
- }
-
- &.showBorder {
- padding: 8px 8px 0;
- }
-`;
-
-TabContainer.displayName = 'TabContainer';
-
export const TabNavigation = React.memo(props => {
- const { display = 'condensed', navTabs, pageName, showBorder, tabName } = props;
+ const { display, navTabs, pageName, tabName } = props;
+
const mapLocationToTab = (): string => {
return getOr(
'',
@@ -44,6 +21,7 @@ export const TabNavigation = React.memo(props => {
Object.values(navTabs).find(item => tabName === item.id || pageName === item.id)
);
};
+
const [selectedTabId, setSelectedTabId] = useState(mapLocationToTab());
useEffect(() => {
const currentTabSelected = mapLocationToTab();
@@ -57,31 +35,21 @@ export const TabNavigation = React.memo(props => {
const renderTabs = (): JSX.Element[] =>
Object.values(navTabs).map(tab => (
- {
+ track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${tab.id}`);
+ }}
>
-
- {
- track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${tab.id}`);
- }}
- >
- {tab.name}
-
-
-
+ {tab.name}
+
));
- return (
-
- {renderTabs()}
-
- );
+
+ return {renderTabs()};
});
+TabNavigation.displayName = 'TabNavigation';
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/types.ts
index 2918a19df52fd..a8e16c82fbf80 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/types.ts
@@ -9,7 +9,6 @@ import { UrlStateType } from '../url_state/constants';
export interface SiemNavigationComponentProps {
display?: 'default' | 'condensed';
navTabs: Record;
- showBorder?: boolean;
}
export type SearchNavTab = NavTab | { urlKey: UrlStateType; isDetailPage: boolean };
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.test.tsx
index 7a0caf14af302..f5207fc6a35fd 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.test.tsx
@@ -492,7 +492,7 @@ describe('StatefulOpenTimeline', () => {
expect(
wrapper
- .find('[data-test-subj="header-panel-title"]')
+ .find('[data-test-subj="header-section-title"]')
.first()
.text()
).toEqual(title);
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.test.tsx
index db3d192f06ba1..9303c09c994aa 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.test.tsx
@@ -30,7 +30,7 @@ describe('TitleRow', () => {
expect(
wrapper
- .find('[data-test-subj="header-panel-title"]')
+ .find('[data-test-subj="header-section-title"]')
.first()
.text()
).toEqual(title);
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.tsx
index f9b107e08afa2..78281a27bb360 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.tsx
@@ -10,7 +10,7 @@ import { pure } from 'recompose';
import * as i18n from '../translations';
import { OpenTimelineProps } from '../types';
-import { HeaderPanel } from '../../header_panel';
+import { HeaderSection } from '../../header_section';
type Props = Pick & {
/** The number of timelines currently selected */
@@ -23,7 +23,7 @@ type Props = Pick(
({ onAddTimelinesToFavorites, onDeleteSelected, selectedTimelinesCount, title }) => (
-
+
{(onAddTimelinesToFavorites || onDeleteSelected) && (
{onAddTimelinesToFavorites && (
@@ -55,7 +55,7 @@ export const TitleRow = pure(
)}
)}
-
+
)
);
diff --git a/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/__snapshots__/index.test.tsx.snap
new file mode 100644
index 0000000000000..caf4334cacf57
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,7 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`HistogramSignals it renders 1`] = `
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.test.tsx
new file mode 100644
index 0000000000000..2412d05f3f47d
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import React from 'react';
+
+import '../../../../mock/ui_settings';
+import { TestProviders } from '../../../../mock';
+import { HistogramSignals } from './index';
+
+jest.mock('../../../../lib/settings/use_kibana_ui_setting');
+
+describe('HistogramSignals', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx
new file mode 100644
index 0000000000000..fa26664930fe5
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx
@@ -0,0 +1,85 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+ Axis,
+ Chart,
+ HistogramBarSeries,
+ Settings,
+ getAxisId,
+ getSpecId,
+ niceTimeFormatByDay,
+ timeFormatter,
+} from '@elastic/charts';
+import React from 'react';
+import { npStart } from 'ui/new_platform';
+
+export const HistogramSignals = React.memo(() => {
+ const sampleChartData = [
+ { x: 1571090784000, y: 2, a: 'a' },
+ { x: 1571090784000, y: 2, b: 'b' },
+ { x: 1571093484000, y: 7, a: 'a' },
+ { x: 1571096184000, y: 3, a: 'a' },
+ { x: 1571098884000, y: 2, a: 'a' },
+ { x: 1571101584000, y: 7, a: 'a' },
+ { x: 1571104284000, y: 3, a: 'a' },
+ { x: 1571106984000, y: 2, a: 'a' },
+ { x: 1571109684000, y: 7, a: 'a' },
+ { x: 1571112384000, y: 3, a: 'a' },
+ { x: 1571115084000, y: 2, a: 'a' },
+ { x: 1571117784000, y: 7, a: 'a' },
+ { x: 1571120484000, y: 3, a: 'a' },
+ { x: 1571123184000, y: 2, a: 'a' },
+ { x: 1571125884000, y: 7, a: 'a' },
+ { x: 1571128584000, y: 3, a: 'a' },
+ { x: 1571131284000, y: 2, a: 'a' },
+ { x: 1571133984000, y: 7, a: 'a' },
+ { x: 1571136684000, y: 3, a: 'a' },
+ { x: 1571139384000, y: 2, a: 'a' },
+ { x: 1571142084000, y: 7, a: 'a' },
+ { x: 1571144784000, y: 3, a: 'a' },
+ { x: 1571147484000, y: 2, a: 'a' },
+ { x: 1571150184000, y: 7, a: 'a' },
+ { x: 1571152884000, y: 3, a: 'a' },
+ { x: 1571155584000, y: 2, a: 'a' },
+ { x: 1571158284000, y: 7, a: 'a' },
+ { x: 1571160984000, y: 3, a: 'a' },
+ { x: 1571163684000, y: 2, a: 'a' },
+ { x: 1571166384000, y: 7, a: 'a' },
+ { x: 1571169084000, y: 3, a: 'a' },
+ { x: 1571171784000, y: 2, a: 'a' },
+ { x: 1571174484000, y: 7, a: 'a' },
+ ];
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+});
+HistogramSignals.displayName = 'HistogramSignals';
diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx
index 3d4a2bc31f2fc..ebabde44c61e9 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx
@@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import React, { useState } from 'react';
import { pure } from 'recompose';
-import { HeaderPanel } from '../../../header_panel';
+import { HeaderSection } from '../../../header_section';
import { manageQuery } from '../../../page/manage_query';
import {
ID as OverviewHostQueryId,
@@ -42,7 +42,7 @@ export const OverviewHost = pure(({ endDate, startDate, setQu
return (
setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
- (({ endDate, startDate, setQu
-
+
{({ overviewHost, loading, id, inspect, refetch }) => (
diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx
index c1629a50341db..b6f1a9cdf26e4 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx
@@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import React, { useState } from 'react';
import { pure } from 'recompose';
-import { HeaderPanel } from '../../../header_panel';
+import { HeaderSection } from '../../../header_section';
import { manageQuery } from '../../../page/manage_query';
import {
ID as OverviewNetworkQueryId,
@@ -42,7 +42,7 @@ export const OverviewNetwork = pure(({ endDate, startDate, setQuery })
return (
setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
- (({ endDate, startDate, setQuery })
defaultMessage="View network"
/>
-
+
{({ overviewNetwork, loading, id, inspect, refetch }) => (
diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx
index 646d003051e83..7be0c1885811b 100644
--- a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx
@@ -35,7 +35,7 @@ import {
import { TlsColumns } from '../page/network/tls_table/columns';
import { UncommonProcessTableColumns } from '../page/hosts/uncommon_process_table';
import { UsersColumns } from '../page/network/users_table/columns';
-import { HeaderPanel } from '../header_panel';
+import { HeaderSection } from '../header_section';
import { Loader } from '../loader';
import { useStateToaster } from '../toasters';
import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants';
@@ -234,7 +234,7 @@ export const PaginatedTable = memo(
onMouseEnter={() => setShowInspect(true)}
onMouseLeave={() => setShowInspect(false)}
>
- (
tooltip={headerTooltip}
>
{!loadingInitial && headerSupplement}
-
+
{loadingInitial ? (
diff --git a/x-pack/legacy/plugins/siem/public/components/progress_inline/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/progress_inline/__snapshots__/index.test.tsx.snap
new file mode 100644
index 0000000000000..c62712e6cfe59
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/progress_inline/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ProgressInline it renders 1`] = `
+
+
+ Test progress
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/progress_inline/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/progress_inline/index.test.tsx
new file mode 100644
index 0000000000000..269bcebdae01a
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/progress_inline/index.test.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import React from 'react';
+
+import { TestProviders } from '../../mock';
+import '../../mock/ui_settings';
+import { ProgressInline } from './index';
+
+jest.mock('../../lib/settings/use_kibana_ui_setting');
+
+describe('ProgressInline', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+ {'Test progress'}
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/progress_inline/index.tsx b/x-pack/legacy/plugins/siem/public/components/progress_inline/index.tsx
new file mode 100644
index 0000000000000..90eca051e3d11
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/progress_inline/index.tsx
@@ -0,0 +1,51 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiProgress } from '@elastic/eui';
+import React from 'react';
+import styled, { css } from 'styled-components';
+
+const Wrapper = styled.dl`
+ ${({ theme }) => css`
+ align-items: center;
+ display: inline-flex;
+
+ & > * + * {
+ margin-left: ${theme.eui.euiSizeS};
+ }
+
+ .siemProgressInline__bar {
+ width: 100px;
+ }
+ `}
+`;
+Wrapper.displayName = 'Wrapper';
+
+export interface ProgressInlineProps {
+ children: string;
+ current: number;
+ max: number;
+ unit: string;
+}
+
+export const ProgressInline = React.memo(
+ ({ children, current, max, unit }) => (
+
+ {children}
+
+
+
+
+
+
+ {current.toLocaleString()}
+ {'/'}
+ {max.toLocaleString()} {unit}
+
+
+ )
+);
+ProgressInline.displayName = 'ProgressInline';
diff --git a/x-pack/legacy/plugins/siem/public/components/subtitle/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/subtitle/__snapshots__/index.test.tsx.snap
new file mode 100644
index 0000000000000..2522d4d1de084
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/subtitle/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Subtitle it renders 1`] = `
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/subtitle/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/subtitle/index.test.tsx
new file mode 100644
index 0000000000000..77506f8a466a5
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/subtitle/index.test.tsx
@@ -0,0 +1,77 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { mount, shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import React from 'react';
+
+import '../../mock/ui_settings';
+import { TestProviders } from '../../mock';
+import { Subtitle } from './index';
+
+jest.mock('../../lib/settings/use_kibana_ui_setting');
+
+describe('Subtitle', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+
+ test('it renders one subtitle string item', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(wrapper.find('.siemSubtitle__item--text').length).toEqual(1);
+ });
+
+ test('it renders multiple subtitle string items', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(wrapper.find('.siemSubtitle__item--text').length).toEqual(2);
+ });
+
+ test('it renders one subtitle React.ReactNode item', () => {
+ const wrapper = mount(
+
+ {'Test subtitle'}} />
+
+ );
+
+ expect(wrapper.find('.siemSubtitle__item--node').length).toEqual(1);
+ });
+
+ test('it renders multiple subtitle React.ReactNode items', () => {
+ const wrapper = mount(
+
+ {'Test subtitle 1'}, {'Test subtitle 2'}]} />
+
+ );
+
+ expect(wrapper.find('.siemSubtitle__item--node').length).toEqual(2);
+ });
+
+ test('it renders multiple subtitle items of mixed type', () => {
+ const wrapper = mount(
+
+ {'Test subtitle 2'}]} />
+
+ );
+
+ expect(wrapper.find('.siemSubtitle__item').length).toEqual(2);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx b/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx
new file mode 100644
index 0000000000000..123e14d239182
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import styled, { css } from 'styled-components';
+
+const Wrapper = styled.div`
+ ${({ theme }) => css`
+ margin-top: ${theme.eui.euiSizeS};
+
+ .siemSubtitle__item {
+ color: ${theme.eui.textColors.subdued};
+ font-size: ${theme.eui.euiFontSizeXS};
+ line-height: ${theme.eui.euiLineHeight};
+
+ @media only screen and (min-width: ${theme.eui.euiBreakpoints.s}) {
+ display: inline-block;
+ margin-right: ${theme.eui.euiSize};
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+ }
+ `}
+`;
+Wrapper.displayName = 'Wrapper';
+
+interface SubtitleItemProps {
+ children: string | React.ReactNode;
+}
+
+const SubtitleItem = React.memo(({ children }) => {
+ if (typeof children === 'string') {
+ return {children}
;
+ } else {
+ return {children}
;
+ }
+});
+SubtitleItem.displayName = 'SubtitleItem';
+
+export interface SubtitleProps {
+ items: string | React.ReactNode | Array;
+}
+
+export const Subtitle = React.memo(({ items }) => {
+ return (
+
+ {Array.isArray(items) ? (
+ items.map((item, i) => {item})
+ ) : (
+ {items}
+ )}
+
+ );
+});
+Subtitle.displayName = 'Subtitle';
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts b/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts
index c709a9370ec61..2e700e3e23b64 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts
@@ -6,17 +6,18 @@
export enum CONSTANTS {
appQuery = 'query',
+ detectionEnginePage = 'detectionEngine.page',
filters = 'filters',
- savedQuery = 'savedQuery',
hostsDetails = 'hosts.details',
hostsPage = 'hosts.page',
networkDetails = 'network.details',
networkPage = 'network.page',
overviewPage = 'overview.page',
+ savedQuery = 'savedQuery',
timelinePage = 'timeline.page',
timerange = 'timerange',
timeline = 'timeline',
unknown = 'unknown',
}
-export type UrlStateType = 'host' | 'network' | 'overview' | 'timeline';
+export type UrlStateType = 'detection-engine' | 'host' | 'network' | 'overview' | 'timeline';
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts
index f7487d7a81a7a..aa340b54c1699 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts
@@ -72,12 +72,14 @@ export const replaceQueryStringInLocation = (location: Location, queryString: st
};
export const getUrlType = (pageName: string): UrlStateType => {
- if (pageName === SiemPageName.hosts) {
+ if (pageName === SiemPageName.overview) {
+ return 'overview';
+ } else if (pageName === SiemPageName.hosts) {
return 'host';
} else if (pageName === SiemPageName.network) {
return 'network';
- } else if (pageName === SiemPageName.overview) {
- return 'overview';
+ } else if (pageName === SiemPageName.detectionEngine) {
+ return 'detection-engine';
} else if (pageName === SiemPageName.timelines) {
return 'timeline';
}
@@ -97,7 +99,9 @@ export const getCurrentLocation = (
pageName: string,
detailName: string | undefined
): LocationTypes => {
- if (pageName === SiemPageName.hosts) {
+ if (pageName === SiemPageName.overview) {
+ return CONSTANTS.overviewPage;
+ } else if (pageName === SiemPageName.hosts) {
if (detailName != null) {
return CONSTANTS.hostsDetails;
}
@@ -107,8 +111,8 @@ export const getCurrentLocation = (
return CONSTANTS.networkDetails;
}
return CONSTANTS.networkPage;
- } else if (pageName === SiemPageName.overview) {
- return CONSTANTS.overviewPage;
+ } else if (pageName === SiemPageName.detectionEngine) {
+ return CONSTANTS.detectionEnginePage;
} else if (pageName === SiemPageName.timelines) {
return CONSTANTS.timelinePage;
}
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts
index 44c050a1990ce..13618125325e1 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts
@@ -25,6 +25,7 @@ export const ALL_URL_STATE_KEYS: KeyUrlState[] = [
];
export const URL_STATE_KEYS: Record = {
+ 'detection-engine': [],
host: [
CONSTANTS.appQuery,
CONSTANTS.filters,
@@ -39,15 +40,16 @@ export const URL_STATE_KEYS: Record = {
CONSTANTS.timerange,
CONSTANTS.timeline,
],
- timeline: [CONSTANTS.timeline, CONSTANTS.timerange],
overview: [CONSTANTS.timeline, CONSTANTS.timerange],
+ timeline: [CONSTANTS.timeline, CONSTANTS.timerange],
};
export type LocationTypes =
- | CONSTANTS.networkDetails
- | CONSTANTS.networkPage
+ | CONSTANTS.detectionEnginePage
| CONSTANTS.hostsDetails
| CONSTANTS.hostsPage
+ | CONSTANTS.networkDetails
+ | CONSTANTS.networkPage
| CONSTANTS.overviewPage
| CONSTANTS.timelinePage
| CONSTANTS.unknown;
diff --git a/x-pack/legacy/plugins/siem/public/components/wrapper_page/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/wrapper_page/__snapshots__/index.test.tsx.snap
new file mode 100644
index 0000000000000..e5311bfb050a3
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/wrapper_page/__snapshots__/index.test.tsx.snap
@@ -0,0 +1,47 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`WrapperPage it renders 1`] = `
+
+
+
+ Test page
+
+
+
+`;
+
+exports[`WrapperPage restrict width custom max width when restrictWidth is number 1`] = `
+
+
+
+ Test page
+
+
+
+`;
+
+exports[`WrapperPage restrict width custom max width when restrictWidth is string 1`] = `
+
+
+
+ Test page
+
+
+
+`;
+
+exports[`WrapperPage restrict width default max width when restrictWidth is true 1`] = `
+
+
+
+ Test page
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.test.tsx
new file mode 100644
index 0000000000000..95e80e8b9e5de
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.test.tsx
@@ -0,0 +1,67 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import React from 'react';
+
+import { TestProviders } from '../../mock';
+import '../../mock/ui_settings';
+import { WrapperPage } from './index';
+
+jest.mock('../../lib/settings/use_kibana_ui_setting');
+
+describe('WrapperPage', () => {
+ test('it renders', () => {
+ const wrapper = shallow(
+
+
+ {'Test page'}
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+
+ describe('restrict width', () => {
+ test('default max width when restrictWidth is true', () => {
+ const wrapper = shallow(
+
+
+ {'Test page'}
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+
+ test('custom max width when restrictWidth is number', () => {
+ const wrapper = shallow(
+
+
+ {'Test page'}
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+
+ test('custom max width when restrictWidth is string', () => {
+ const wrapper = shallow(
+
+
+ {'Test page'}
+
+
+ );
+
+ expect(toJson(wrapper)).toMatchSnapshot();
+ });
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.tsx b/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.tsx
new file mode 100644
index 0000000000000..5998aa527206e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.tsx
@@ -0,0 +1,61 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import classNames from 'classnames';
+import React from 'react';
+import styled, { css } from 'styled-components';
+
+import { gutterTimeline } from '../../lib/helpers';
+
+const Wrapper = styled.div`
+ ${({ theme }) => css`
+ padding: ${theme.eui.paddingSizes.l} ${gutterTimeline} ${theme.eui.paddingSizes.l}
+ ${theme.eui.paddingSizes.l};
+
+ &.siemWrapperPage--restrictWidthDefault,
+ &.siemWrapperPage--restrictWidthCustom {
+ box-sizing: content-box;
+ margin: 0 auto;
+ }
+
+ &.siemWrapperPage--restrictWidthDefault {
+ max-width: 1000px;
+ }
+ `}
+`;
+Wrapper.displayName = 'Wrapper';
+
+export interface WrapperPageProps {
+ children: React.ReactNode;
+ className?: string;
+ restrictWidth?: boolean | number | string;
+ style?: Record;
+}
+
+export const WrapperPage = React.memo(
+ ({ children, className, restrictWidth, style }) => {
+ const classes = classNames(className, {
+ siemWrapperPage: true,
+ 'siemWrapperPage--restrictWidthDefault':
+ restrictWidth && typeof restrictWidth === 'boolean' && restrictWidth === true,
+ 'siemWrapperPage--restrictWidthCustom': restrictWidth && typeof restrictWidth !== 'boolean',
+ });
+
+ let customStyle: WrapperPageProps['style'];
+
+ if (restrictWidth && typeof restrictWidth !== 'boolean') {
+ const value = typeof restrictWidth === 'number' ? `${restrictWidth}px` : restrictWidth;
+ customStyle = { ...style, maxWidth: value };
+ }
+
+ return (
+
+ {children}
+
+ );
+ }
+);
+WrapperPage.displayName = 'WrapperPage';
diff --git a/x-pack/legacy/plugins/siem/public/lib/helpers/index.tsx b/x-pack/legacy/plugins/siem/public/lib/helpers/index.tsx
index 659ecbadc34d2..5706dcc50ed25 100644
--- a/x-pack/legacy/plugins/siem/public/lib/helpers/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/lib/helpers/index.tsx
@@ -42,3 +42,9 @@ export const assertUnreachable = (
): never => {
throw new Error(`${message}: ${x}`);
};
+
+/**
+ * Global variables
+ */
+
+export const gutterTimeline = '70px'; // Michael: Temporary until timeline is moved.
diff --git a/x-pack/legacy/plugins/siem/public/pages/404.tsx b/x-pack/legacy/plugins/siem/public/pages/404.tsx
index 58a3c904b89a0..f806a5a7fcdd3 100644
--- a/x-pack/legacy/plugins/siem/public/pages/404.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/404.tsx
@@ -8,13 +8,14 @@ import React from 'react';
import { pure } from 'recompose';
import { FormattedMessage } from '@kbn/i18n/react';
+import { WrapperPage } from '../components/wrapper_page';
+
export const NotFoundPage = pure(() => (
-
+
-
+
));
-
NotFoundPage.displayName = 'NotFoundPage';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/index.tsx
new file mode 100644
index 0000000000000..47a3527aff99c
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/index.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+import { HeaderPage } from '../../../components/header_page';
+import { WrapperPage } from '../../../components/wrapper_page';
+import { SpyRoute } from '../../../utils/route/spy_routes';
+import * as i18n from './translations';
+
+export const CreateRuleComponent = React.memo(() => {
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+});
+CreateRuleComponent.displayName = 'CreateRuleComponent';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/translations.ts
new file mode 100644
index 0000000000000..884f3f3741228
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/translations.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.createRule.pageTitle', {
+ defaultMessage: 'Create new rule',
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx
new file mode 100644
index 0000000000000..9b63a6e160e42
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx
@@ -0,0 +1,205 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+ EuiButton,
+ EuiFilterButton,
+ EuiFilterGroup,
+ EuiPanel,
+ EuiSelect,
+ EuiSpacer,
+} from '@elastic/eui';
+import React, { useState } from 'react';
+import { StickyContainer } from 'react-sticky';
+
+import { FiltersGlobal } from '../../components/filters_global';
+import { HeaderPage } from '../../components/header_page';
+import { HeaderSection } from '../../components/header_section';
+import { HistogramSignals } from '../../components/page/detection_engine/histogram_signals';
+import { SiemSearchBar } from '../../components/search_bar';
+import {
+ UtilityBar,
+ UtilityBarAction,
+ UtilityBarGroup,
+ UtilityBarSection,
+ UtilityBarText,
+} from '../../components/detection_engine/utility_bar';
+import { WrapperPage } from '../../components/wrapper_page';
+import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source';
+import { SpyRoute } from '../../utils/route/spy_routes';
+import { DetectionEngineEmptyPage } from './detection_engine_empty_page';
+import * as i18n from './translations';
+
+const OpenSignals = React.memo(() => {
+ return (
+ <>
+
+
+
+ {`${i18n.PANEL_SUBTITLE_SHOWING}: 7,712 signals`}
+
+
+
+ {'Selected: 20 signals'}
+
+ {'Batch actions context menu here.'}}
+ >
+ {'Batch actions'}
+
+
+
+ {'Select all signals on all pages'}
+
+
+
+
+ {'Clear 7 filters'}
+
+ {'Clear aggregation'}
+
+
+
+
+
+ {'Customize columns context menu here.'}}
+ >
+ {'Customize columns'}
+
+
+ {'Aggregate data'}
+
+
+
+
+ {/* Michael: Open signals datagrid here. Talk to Chandler Prall about possibility of early access. If not possible, use basic table. */}
+ >
+ );
+});
+
+const ClosedSignals = React.memo(() => {
+ return (
+ <>
+
+
+
+ {`${i18n.PANEL_SUBTITLE_SHOWING}: 7,712 signals`}
+
+
+
+
+
+ {'Customize columns context menu here.'}}
+ >
+ {'Customize columns'}
+
+
+ {'Aggregate data'}
+
+
+
+
+ {/* Michael: Closed signals datagrid here. Talk to Chandler Prall about possibility of early access. If not possible, use basic table. */}
+ >
+ );
+});
+
+export const DetectionEngineComponent = React.memo(() => {
+ const sampleChartOptions = [
+ { text: 'Risk scores', value: 'risk_scores' },
+ { text: 'Severities', value: 'severities' },
+ { text: 'Top destination IPs', value: 'destination_ips' },
+ { text: 'Top event actions', value: 'event_actions' },
+ { text: 'Top event categories', value: 'event_categories' },
+ { text: 'Top host names', value: 'host_names' },
+ { text: 'Top rule types', value: 'rule_types' },
+ { text: 'Top rules', value: 'rules' },
+ { text: 'Top source IPs', value: 'source_ips' },
+ { text: 'Top users', value: 'users' },
+ ];
+
+ const filterGroupOptions = ['open', 'closed'];
+ const [filterGroupState, setFilterGroupState] = useState(filterGroupOptions[0]);
+
+ return (
+ <>
+
+ {({ indicesExist, indexPattern }) => {
+ return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
+
+
+
+
+
+
+
+
+ {i18n.BUTTON_MANAGE_RULES}
+
+
+
+
+
+ {}}
+ prepend="Stack by"
+ value={sampleChartOptions[0].value}
+ />
+
+
+
+
+
+
+
+
+
+
+ setFilterGroupState(filterGroupOptions[0])}
+ withNext
+ >
+ {'Open signals'}
+
+
+ setFilterGroupState(filterGroupOptions[1])}
+ >
+ {'Closed signals'}
+
+
+
+
+ {filterGroupState === filterGroupOptions[0] ? : }
+
+
+
+ ) : (
+
+
+
+
+
+ );
+ }}
+
+
+
+ >
+ );
+});
+DetectionEngineComponent.displayName = 'DetectionEngineComponent';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx
new file mode 100644
index 0000000000000..cb3e690615395
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import chrome from 'ui/chrome';
+import { documentationLinks } from 'ui/documentation_links';
+
+import { EmptyPage } from '../../components/empty_page';
+import * as i18n from './translations';
+
+const basePath = chrome.getBasePath();
+
+export const DetectionEngineEmptyPage = React.memo(() => (
+
+));
+DetectionEngineEmptyPage.displayName = 'DetectionEngineEmptyPage';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/index.tsx
new file mode 100644
index 0000000000000..9b8607fdc7685
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/index.tsx
@@ -0,0 +1,128 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+ EuiButton,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiPanel,
+ EuiSpacer,
+ EuiTabbedContent,
+} from '@elastic/eui';
+import React from 'react';
+
+import { HeaderPage } from '../../../components/header_page';
+import { HeaderSection } from '../../../components/header_section';
+import { WrapperPage } from '../../../components/wrapper_page';
+import { SpyRoute } from '../../../utils/route/spy_routes';
+import * as i18n from './translations';
+
+const Define = React.memo(() => (
+ <>
+
+
+
+
+
+ >
+));
+Define.displayName = 'Define';
+
+const About = React.memo(() => (
+ <>
+
+
+
+
+
+ >
+));
+About.displayName = 'About';
+
+const Schedule = React.memo(() => (
+ <>
+
+
+
+
+
+ >
+));
+Schedule.displayName = 'Schedule';
+
+export const EditRuleComponent = React.memo(() => {
+ return (
+ <>
+
+
+
+
+
+ {'Cancel'}
+
+
+
+
+
+ {'Save changes'}
+
+
+
+
+
+ ,
+ },
+ {
+ id: 'tabAbout',
+ name: 'About',
+ content: ,
+ },
+ {
+ id: 'tabSchedule',
+ name: 'Schedule',
+ content: ,
+ },
+ ]}
+ />
+
+
+
+
+
+
+ {'Cancel'}
+
+
+
+
+
+ {'Save changes'}
+
+
+
+
+
+
+ >
+ );
+});
+EditRuleComponent.displayName = 'EditRuleComponent';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/translations.ts
new file mode 100644
index 0000000000000..cc2e2565eb8d0
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/translations.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.editRule.pageTitle', {
+ defaultMessage: 'Edit rule settings',
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx
new file mode 100644
index 0000000000000..90524b4da0af4
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom';
+
+import { CreateRuleComponent } from './create_rule';
+import { DetectionEngineComponent } from './detection_engine';
+import { EditRuleComponent } from './edit_rule';
+import { RuleDetailsComponent } from './rule_details';
+import { RulesComponent } from './rules';
+
+const detectionEnginePath = `/:pageName(detection-engine)`;
+
+type Props = Partial> & { url: string };
+
+export const DetectionEngineContainer = React.memo(() => (
+
+ } strict />
+ } />
+ }
+ />
+ }
+ />
+ }
+ />
+ (
+
+ )}
+ />
+
+));
+DetectionEngineContainer.displayName = 'DetectionEngineContainer';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/index.tsx
new file mode 100644
index 0000000000000..da3e5fb2083dd
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/index.tsx
@@ -0,0 +1,660 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+ EuiBasicTable,
+ EuiButton,
+ EuiButtonIcon,
+ EuiCallOut,
+ EuiFilterButton,
+ EuiFilterGroup,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiIconTip,
+ EuiPanel,
+ EuiPopover,
+ EuiSelect,
+ EuiSpacer,
+ EuiSwitch,
+ EuiTabbedContent,
+ EuiTextColor,
+} from '@elastic/eui';
+import moment from 'moment';
+import React, { useState } from 'react';
+import { StickyContainer } from 'react-sticky';
+
+import { getEmptyTagValue } from '../../../components/empty_value';
+import { FiltersGlobal } from '../../../components/filters_global';
+import { HeaderPage } from '../../../components/header_page';
+import { HeaderSection } from '../../../components/header_section';
+import { HistogramSignals } from '../../../components/page/detection_engine/histogram_signals';
+import { ProgressInline } from '../../../components/progress_inline';
+import { SiemSearchBar } from '../../../components/search_bar';
+import {
+ UtilityBar,
+ UtilityBarAction,
+ UtilityBarGroup,
+ UtilityBarSection,
+ UtilityBarText,
+} from '../../../components/detection_engine/utility_bar';
+import { WrapperPage } from '../../../components/wrapper_page';
+import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../containers/source';
+import { SpyRoute } from '../../../utils/route/spy_routes';
+import { DetectionEngineEmptyPage } from '../detection_engine_empty_page';
+import * as i18n from './translations';
+
+// Michael: Will need to change this to get the current datetime format from Kibana settings.
+const dateTimeFormat = (value: string) => {
+ return moment(value).format('M/D/YYYY, h:mm A');
+};
+
+const OpenSignals = React.memo(() => {
+ return (
+ <>
+
+
+
+ {'Showing: 439 signals'}
+
+
+
+ {'Selected: 20 signals'}
+
+ {'Batch actions context menu here.'}}
+ >
+ {'Batch actions'}
+
+
+
+ {'Select all signals on all pages'}
+
+
+
+
+ {'Clear 7 filters'}
+
+ {'Clear aggregation'}
+
+
+
+
+
+ {'Customize columns context menu here.'}}
+ >
+ {'Customize columns'}
+
+
+ {'Aggregate data'}
+
+
+
+
+ {/* Michael: Open signals datagrid here. Talk to Chandler Prall about possibility of early access. If not possible, use basic table. */}
+ >
+ );
+});
+
+const ClosedSignals = React.memo(() => {
+ return (
+ <>
+
+
+
+ {'Showing: 439 signals'}
+
+
+
+
+
+ {'Customize columns context menu here.'}}
+ >
+ {'Customize columns'}
+
+
+ {'Aggregate data'}
+
+
+
+
+ {/* Michael: Closed signals datagrid here. Talk to Chandler Prall about possibility of early access. If not possible, use basic table. */}
+ >
+ );
+});
+
+const Signals = React.memo(() => {
+ const sampleChartOptions = [
+ { text: 'Risk scores', value: 'risk_scores' },
+ { text: 'Severities', value: 'severities' },
+ { text: 'Top destination IPs', value: 'destination_ips' },
+ { text: 'Top event actions', value: 'event_actions' },
+ { text: 'Top event categories', value: 'event_categories' },
+ { text: 'Top host names', value: 'host_names' },
+ { text: 'Top source IPs', value: 'source_ips' },
+ { text: 'Top users', value: 'users' },
+ ];
+
+ const filterGroupOptions = ['open', 'closed'];
+ const [filterGroupState, setFilterGroupState] = useState(filterGroupOptions[0]);
+
+ return (
+ <>
+
+
+
+
+ {}}
+ prepend="Stack by"
+ value={sampleChartOptions[0].value}
+ />
+
+
+
+
+
+
+
+
+
+
+ setFilterGroupState(filterGroupOptions[0])}
+ withNext
+ >
+ {'Open signals'}
+
+
+ setFilterGroupState(filterGroupOptions[1])}
+ >
+ {'Closed signals'}
+
+
+
+
+ {filterGroupState === filterGroupOptions[0] ? : }
+
+ >
+ );
+});
+Signals.displayName = 'Signals';
+
+const ActivityMonitor = React.memo(() => {
+ interface ColumnTypes {
+ id: number;
+ ran: string;
+ lookedBackTo: string;
+ status: string;
+ response: string | undefined;
+ }
+
+ interface PageTypes {
+ index: number;
+ size: number;
+ }
+
+ interface SortTypes {
+ field: string;
+ direction: string;
+ }
+
+ const actions = [
+ {
+ available: (item: ColumnTypes) => item.status === 'Running',
+ description: 'Stop',
+ icon: 'stop',
+ isPrimary: true,
+ name: 'Stop',
+ onClick: () => {},
+ type: 'icon',
+ },
+ {
+ available: (item: ColumnTypes) => item.status === 'Stopped',
+ description: 'Resume',
+ icon: 'play',
+ isPrimary: true,
+ name: 'Resume',
+ onClick: () => {},
+ type: 'icon',
+ },
+ ];
+
+ // Michael: Are we able to do custom, in-table-header filters, as shown in my wireframes?
+ const columns = [
+ {
+ field: 'ran',
+ name: 'Ran',
+ render: (value: ColumnTypes['ran']) => ,
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'lookedBackTo',
+ name: 'Looked back to',
+ render: (value: ColumnTypes['lookedBackTo']) => (
+
+ ),
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'status',
+ name: 'Status',
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'response',
+ name: 'Response',
+ render: (value: ColumnTypes['response']) => {
+ return value === undefined ? (
+ getEmptyTagValue()
+ ) : (
+ <>
+ {value === 'Fail' ? (
+
+ {value}
+
+ ) : (
+ {value}
+ )}
+ >
+ );
+ },
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ actions,
+ width: '40px',
+ },
+ ];
+
+ const sampleTableData = [
+ {
+ id: 1,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Running',
+ },
+ {
+ id: 2,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Stopped',
+ },
+ {
+ id: 3,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Fail',
+ },
+ {
+ id: 4,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 5,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 6,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 7,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 8,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 9,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 10,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 11,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 12,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 13,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 14,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 15,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 16,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 17,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 18,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 19,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 20,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 21,
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ ];
+
+ const [itemsTotalState] = useState(sampleTableData.length);
+ const [pageState, setPageState] = useState({ index: 0, size: 20 });
+ // const [selectedState, setSelectedState] = useState([]);
+ const [sortState, setSortState] = useState({ field: 'ran', direction: 'desc' });
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {'Showing: 39 activites'}
+
+
+
+ {'Selected: 2 activities'}
+
+ {'Stop selected'}
+
+
+
+ {'Clear 7 filters'}
+
+
+
+
+ {
+ setPageState(page);
+ setSortState(sort);
+ }}
+ pagination={{
+ pageIndex: pageState.index,
+ pageSize: pageState.size,
+ totalItemCount: itemsTotalState,
+ pageSizeOptions: [5, 10, 20],
+ }}
+ selection={{
+ selectable: (item: ColumnTypes) => item.status !== 'Completed',
+ selectableMessage: (selectable: boolean) =>
+ selectable ? undefined : 'Completed runs cannot be acted upon',
+ onSelectionChange: (selectedItems: ColumnTypes[]) => {
+ // setSelectedState(selectedItems);
+ },
+ }}
+ sorting={{
+ sort: sortState,
+ }}
+ />
+
+ >
+ );
+});
+ActivityMonitor.displayName = 'ActivityMonitor';
+
+export const RuleDetailsComponent = React.memo(() => {
+ const [popoverState, setPopoverState] = useState(false);
+
+ return (
+ <>
+
+ {({ indicesExist, indexPattern }) => {
+ return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
+
+
+
+
+
+
+
+ {'Status: Running'}
+ ,
+ ]}
+ title="Automated exfiltration"
+ >
+
+
+ {}} />
+
+
+
+
+
+
+ {'Edit rule settings'}
+
+
+
+
+ setPopoverState(!popoverState)}
+ />
+ }
+ closePopover={() => setPopoverState(false)}
+ isOpen={popoverState}
+ >
+ {'Overflow context menu here.'}
+
+
+
+
+
+
+
+
+ {'Full fail message here.'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* {'Description'}
*/}
+
+ {/*
+
+ {'Description'}
+
+
+
+ {'Severity'}
+
+
+
+ {'Risk score boost'}
+
+
+
+ {'References'}
+
+
+
+ {'False positives'}
+
+
+
+ {'Mitre ATT&CK types'}
+
+
+
+ {'Tags'}
+
+ */}
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ },
+ {
+ id: 'tabActivityMonitor',
+ name: 'Activity monitor',
+ content: ,
+ },
+ ]}
+ />
+
+
+ ) : (
+
+
+
+
+
+ );
+ }}
+
+
+
+ >
+ );
+});
+RuleDetailsComponent.displayName = 'RuleDetailsComponent';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/translations.ts
new file mode 100644
index 0000000000000..3dd5945ff597c
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/translations.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.ruleDetails.pageTitle', {
+ defaultMessage: 'Rule details',
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx
new file mode 100644
index 0000000000000..41a6cf54ff5ab
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx
@@ -0,0 +1,1076 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
+import {
+ EuiBadge,
+ EuiBasicTable,
+ EuiButton,
+ EuiFieldSearch,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiHealth,
+ EuiIconTip,
+ EuiLink,
+ EuiPanel,
+ EuiSpacer,
+ EuiSwitch,
+ EuiTabbedContent,
+ EuiTextColor,
+} from '@elastic/eui';
+import moment from 'moment';
+import React, { useState } from 'react';
+
+import { getEmptyTagValue } from '../../../components/empty_value';
+import { HeaderPage } from '../../../components/header_page';
+import { HeaderSection } from '../../../components/header_section';
+import {
+ UtilityBar,
+ UtilityBarAction,
+ UtilityBarGroup,
+ UtilityBarSection,
+ UtilityBarText,
+} from '../../../components/detection_engine/utility_bar';
+import { WrapperPage } from '../../../components/wrapper_page';
+import { SpyRoute } from '../../../utils/route/spy_routes';
+import * as i18n from './translations';
+
+// Michael: Will need to change this to get the current datetime format from Kibana settings.
+const dateTimeFormat = (value: string) => {
+ return moment(value).format('M/D/YYYY, h:mm A');
+};
+
+const AllRules = React.memo(() => {
+ interface RuleTypes {
+ href: string;
+ name: string;
+ status: string;
+ }
+
+ interface LastResponseTypes {
+ type: string;
+ message?: string;
+ }
+
+ interface ColumnTypes {
+ id: number;
+ rule: RuleTypes;
+ method: string;
+ severity: string;
+ lastCompletedRun: string;
+ lastResponse: LastResponseTypes;
+ tags: string | string[];
+ activate: boolean;
+ }
+
+ interface PageTypes {
+ index: number;
+ size: number;
+ }
+
+ interface SortTypes {
+ field: string;
+ direction: string;
+ }
+
+ const actions = [
+ {
+ description: 'Edit rule settings',
+ icon: 'visControls',
+ name: 'Edit rule settings',
+ onClick: () => {},
+ },
+ {
+ description: 'Run rule manually…',
+ icon: 'play',
+ name: 'Run rule manually…',
+ onClick: () => {},
+ },
+ {
+ description: 'Duplicate rule…',
+ icon: 'copy',
+ name: 'Duplicate rule…',
+ onClick: () => {},
+ },
+ {
+ description: 'Export rule',
+ icon: 'exportAction',
+ name: 'Export rule',
+ onClick: () => {},
+ },
+ {
+ description: 'Delete rule…',
+ icon: 'trash',
+ name: 'Delete rule…',
+ onClick: () => {},
+ },
+ ];
+
+ // Michael: Are we able to do custom, in-table-header filters, as shown in my wireframes?
+ const columns = [
+ {
+ field: 'rule',
+ name: 'Rule',
+ render: (value: ColumnTypes['rule']) => (
+
+ {value.name}{' '}
+ {value.status}
+
+ ),
+ sortable: true,
+ truncateText: true,
+ width: '24%',
+ },
+ {
+ field: 'method',
+ name: 'Method',
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'severity',
+ name: 'Severity',
+ render: (value: ColumnTypes['severity']) => (
+
+ {value}
+
+ ),
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'lastCompletedRun',
+ name: 'Last completed run',
+ render: (value: ColumnTypes['lastCompletedRun']) => {
+ return value === undefined ? (
+ getEmptyTagValue()
+ ) : (
+
+ );
+ },
+ sortable: true,
+ truncateText: true,
+ width: '16%',
+ },
+ {
+ field: 'lastResponse',
+ name: 'Last response',
+ render: (value: ColumnTypes['lastResponse']) => {
+ return value === undefined ? (
+ getEmptyTagValue()
+ ) : (
+ <>
+ {value.type === 'Fail' ? (
+
+ {value.type}
+
+ ) : (
+ {value.type}
+ )}
+ >
+ );
+ },
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'tags',
+ name: 'Tags',
+ render: (value: ColumnTypes['tags']) => (
+
+ {typeof value !== 'string' ? (
+ <>
+ {value.map((tag, i) => (
+
+ {tag}
+
+ ))}
+ >
+ ) : (
+ {value}
+ )}
+
+ ),
+ sortable: true,
+ truncateText: true,
+ width: '20%',
+ },
+ {
+ align: 'center',
+ field: 'activate',
+ name: 'Activate',
+ render: (value: ColumnTypes['activate']) => (
+ // Michael: Uncomment props below when EUI 14.9.0 is added to Kibana.
+ {}} showLabel={false} />
+ ),
+ sortable: true,
+ width: '65px',
+ },
+ {
+ actions,
+ width: '40px',
+ },
+ ];
+
+ const sampleTableData = [
+ {
+ id: 1,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Low',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: ['attack.t1234', 'attack.t4321'],
+ activate: true,
+ },
+ {
+ id: 2,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Medium',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Fail',
+ message: 'Full fail message here.',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 3,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'High',
+ tags: 'attack.t1234',
+ activate: false,
+ },
+ {
+ id: 4,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 5,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 6,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 7,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 8,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 9,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 10,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 11,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 12,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 13,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 14,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 15,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 16,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 17,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 18,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 19,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 20,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ {
+ id: 21,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ status: 'Experimental',
+ },
+ method: 'Custom query',
+ severity: 'Critical',
+ lastCompletedRun: '2019-12-28 00:00:00.000-05:00',
+ lastResponse: {
+ type: 'Success',
+ },
+ tags: 'attack.t1234',
+ activate: true,
+ },
+ ];
+
+ const [itemsTotalState] = useState(sampleTableData.length);
+ const [pageState, setPageState] = useState({ index: 0, size: 20 });
+ // const [selectedState, setSelectedState] = useState([]);
+ const [sortState, setSortState] = useState({ field: 'rule', direction: 'asc' });
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ {'Showing: 39 rules'}
+
+
+
+ {'Selected: 2 rules'}
+
+ {'Batch actions context menu here.'}}
+ >
+ {'Batch actions'}
+
+
+
+
+ {'Clear 7 filters'}
+
+
+
+
+ {
+ setPageState(page);
+ setSortState(sort);
+ }}
+ pagination={{
+ pageIndex: pageState.index,
+ pageSize: pageState.size,
+ totalItemCount: itemsTotalState,
+ pageSizeOptions: [5, 10, 20],
+ }}
+ selection={{
+ selectable: () => true,
+ onSelectionChange: (selectedItems: ColumnTypes[]) => {
+ // setSelectedState(selectedItems);
+ },
+ }}
+ sorting={{
+ sort: sortState,
+ }}
+ />
+
+ >
+ );
+});
+AllRules.displayName = 'AllRules';
+
+const ActivityMonitor = React.memo(() => {
+ interface RuleTypes {
+ href: string;
+ name: string;
+ }
+
+ interface ColumnTypes {
+ id: number;
+ rule: RuleTypes;
+ ran: string;
+ lookedBackTo: string;
+ status: string;
+ response: string | undefined;
+ }
+
+ interface PageTypes {
+ index: number;
+ size: number;
+ }
+
+ interface SortTypes {
+ field: string;
+ direction: string;
+ }
+
+ const actions = [
+ {
+ available: (item: ColumnTypes) => item.status === 'Running',
+ description: 'Stop',
+ icon: 'stop',
+ isPrimary: true,
+ name: 'Stop',
+ onClick: () => {},
+ type: 'icon',
+ },
+ {
+ available: (item: ColumnTypes) => item.status === 'Stopped',
+ description: 'Resume',
+ icon: 'play',
+ isPrimary: true,
+ name: 'Resume',
+ onClick: () => {},
+ type: 'icon',
+ },
+ ];
+
+ // Michael: Are we able to do custom, in-table-header filters, as shown in my wireframes?
+ const columns = [
+ {
+ field: 'rule',
+ name: 'Rule',
+ render: (value: ColumnTypes['rule']) => {value.name},
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'ran',
+ name: 'Ran',
+ render: (value: ColumnTypes['ran']) => ,
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'lookedBackTo',
+ name: 'Looked back to',
+ render: (value: ColumnTypes['lookedBackTo']) => (
+
+ ),
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'status',
+ name: 'Status',
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'response',
+ name: 'Response',
+ render: (value: ColumnTypes['response']) => {
+ return value === undefined ? (
+ getEmptyTagValue()
+ ) : (
+ <>
+ {value === 'Fail' ? (
+
+ {value}
+
+ ) : (
+ {value}
+ )}
+ >
+ );
+ },
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ actions,
+ width: '40px',
+ },
+ ];
+
+ const sampleTableData = [
+ {
+ id: 1,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Running',
+ },
+ {
+ id: 2,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Stopped',
+ },
+ {
+ id: 3,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Fail',
+ },
+ {
+ id: 4,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 5,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 6,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 7,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 8,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 9,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 10,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 11,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 12,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 13,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 14,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 15,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 16,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 17,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 18,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 19,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 20,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ {
+ id: 21,
+ rule: {
+ href: '#/detection-engine/rules/rule-details',
+ name: 'Automated exfiltration',
+ },
+ ran: '2019-12-28 00:00:00.000-05:00',
+ lookedBackTo: '2019-12-28 00:00:00.000-05:00',
+ status: 'Completed',
+ response: 'Success',
+ },
+ ];
+
+ const [itemsTotalState] = useState(sampleTableData.length);
+ const [pageState, setPageState] = useState({ index: 0, size: 20 });
+ // const [selectedState, setSelectedState] = useState([]);
+ const [sortState, setSortState] = useState({ field: 'ran', direction: 'desc' });
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {'Showing: 39 activites'}
+
+
+
+ {'Selected: 2 activities'}
+
+ {'Stop selected'}
+
+
+
+ {'Clear 7 filters'}
+
+
+
+
+ {
+ setPageState(page);
+ setSortState(sort);
+ }}
+ pagination={{
+ pageIndex: pageState.index,
+ pageSize: pageState.size,
+ totalItemCount: itemsTotalState,
+ pageSizeOptions: [5, 10, 20],
+ }}
+ selection={{
+ selectable: (item: ColumnTypes) => item.status !== 'Completed',
+ selectableMessage: (selectable: boolean) =>
+ selectable ? undefined : 'Completed runs cannot be acted upon',
+ onSelectionChange: (selectedItems: ColumnTypes[]) => {
+ // setSelectedState(selectedItems);
+ },
+ }}
+ sorting={{
+ sort: sortState,
+ }}
+ />
+
+ >
+ );
+});
+ActivityMonitor.displayName = 'ActivityMonitor';
+
+export const RulesComponent = React.memo(() => {
+ return (
+ <>
+
+
+
+
+
+ {'Import rule…'}
+
+
+
+
+
+ {'Add new rule'}
+
+
+
+
+
+ ,
+ },
+ {
+ id: 'tabActivityMonitor',
+ name: 'Activity monitor',
+ content: ,
+ },
+ ]}
+ />
+
+
+
+ >
+ );
+});
+RulesComponent.displayName = 'RulesComponent';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts
new file mode 100644
index 0000000000000..2b20c726d4b3f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.rules.pageTitle', {
+ defaultMessage: 'Rules',
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts
new file mode 100644
index 0000000000000..a7e7fa5133a64
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.pageTitle', {
+ defaultMessage: 'Detection engine',
+});
+
+export const PAGE_SUBTITLE = i18n.translate('xpack.siem.detectionEngine.pageSubtitle', {
+ defaultMessage: 'Last signal: X minutes ago',
+});
+
+export const BUTTON_MANAGE_RULES = i18n.translate('xpack.siem.detectionEngine.buttonManageRules', {
+ defaultMessage: 'Manage rules',
+});
+
+export const PANEL_SUBTITLE_SHOWING = i18n.translate(
+ 'xpack.siem.detectionEngine.panelSubtitleShowing',
+ {
+ defaultMessage: 'Showing',
+ }
+);
+
+export const EMPTY_TITLE = i18n.translate('xpack.siem.detectionEngine.emptyTitle', {
+ defaultMessage:
+ 'It looks like you don’t have any indices relevant to the detction engine in the SIEM application',
+});
+
+export const EMPTY_ACTION_PRIMARY = i18n.translate(
+ 'xpack.siem.detectionEngine.emptyActionPrimary',
+ {
+ defaultMessage: 'View setup instructions',
+ }
+);
+
+export const EMPTY_ACTION_SECONDARY = i18n.translate(
+ 'xpack.siem.detectionEngine.emptyActionSecondary',
+ {
+ defaultMessage: 'Go to documentation',
+ }
+);
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx b/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx
index 53bcac028b877..220f8a958aa43 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx
@@ -3,14 +3,16 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import * as i18n from './translations';
-import { SiemPageName, SiemNavTab } from './types';
+
import {
+ getDetectionEngineUrl,
getOverviewUrl,
getNetworkUrl,
getTimelinesUrl,
getHostsUrl,
} from '../../components/link_to';
+import * as i18n from './translations';
+import { SiemPageName, SiemNavTab } from './types';
export const navTabs: SiemNavTab = {
[SiemPageName.overview]: {
@@ -34,6 +36,13 @@ export const navTabs: SiemNavTab = {
disabled: false,
urlKey: 'network',
},
+ [SiemPageName.detectionEngine]: {
+ id: SiemPageName.detectionEngine,
+ name: i18n.DETECTION_ENGINE,
+ href: getDetectionEngineUrl(),
+ disabled: false,
+ urlKey: 'detection-engine',
+ },
[SiemPageName.timelines]: {
id: SiemPageName.timelines,
name: i18n.TIMELINES,
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
index da53ac8fceac4..eb816876bdba8 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
@@ -4,35 +4,32 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPage, EuiPageBody } from '@elastic/eui';
-import { FormattedMessage } from '@kbn/i18n/react';
import * as React from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import { pure } from 'recompose';
import styled from 'styled-components';
-import { i18n } from '@kbn/i18n';
import { AutoSizer } from '../../components/auto_sizer';
import { DragDropContextWrapper } from '../../components/drag_and_drop/drag_drop_context_wrapper';
import { Flyout, flyoutHeaderHeight } from '../../components/flyout';
+import { HeaderGlobal } from '../../components/header_global';
import { HelpMenu } from '../../components/help_menu';
import { LinkToPage } from '../../components/link_to';
-import { SiemNavigation } from '../../components/navigation';
+import { MlHostConditionalContainer } from '../../components/ml/conditional_links/ml_host_conditional_container';
+import { MlNetworkConditionalContainer } from '../../components/ml/conditional_links/ml_network_conditional_container';
import { StatefulTimeline } from '../../components/timeline';
import { AutoSaveWarningMsg } from '../../components/timeline/auto_save_warning';
+import { UseUrlState } from '../../components/url_state';
+import { WithSource } from '../../containers/source';
+import { SpyRoute } from '../../utils/route/spy_routes';
import { NotFoundPage } from '../404';
+import { DetectionEngineContainer } from '../detection_engine';
import { HostsContainer } from '../hosts';
import { NetworkContainer } from '../network';
import { Overview } from '../overview';
import { Timelines } from '../timelines';
-import { WithSource } from '../../containers/source';
-import { MlPopover } from '../../components/ml_popover/ml_popover';
-import { MlHostConditionalContainer } from '../../components/ml/conditional_links/ml_host_conditional_container';
-import { MlNetworkConditionalContainer } from '../../components/ml/conditional_links/ml_network_conditional_container';
import { navTabs } from './home_navigations';
import { SiemPageName } from './types';
-import { UseUrlState } from '../../components/url_state';
-import { SpyRoute } from '../../utils/route/spy_routes';
/*
* This is import is important to keep because if we do not have it
@@ -44,30 +41,8 @@ import 'uiExports/embeddableFactories';
const WrappedByAutoSizer = styled.div`
height: 100%;
`;
-
WrappedByAutoSizer.displayName = 'WrappedByAutoSizer';
-const gutterTimeline = '70px'; // Temporary until timeline is moved - MichaelMarcialis
-
-const Page = styled(EuiPage)`
- ${({ theme }) => `
- padding: 0 ${gutterTimeline} ${theme.eui.euiSizeL} ${theme.eui.euiSizeL};
- `}
-`;
-
-Page.displayName = 'Page';
-
-const NavGlobal = styled.nav`
- ${({ theme }) => `
- background: ${theme.eui.euiColorEmptyShade};
- border-bottom: ${theme.eui.euiBorderThin};
- margin: 0 -${gutterTimeline} 0 -${theme.eui.euiSizeL};
- padding: ${theme.eui.euiSize} ${gutterTimeline} ${theme.eui.euiSize} ${theme.eui.euiSizeL};
- `}
-`;
-
-NavGlobal.displayName = 'NavGlobal';
-
const usersViewing = ['elastic']; // TODO: get the users viewing this timeline from Elasticsearch (persistance)
/** the global Kibana navigation at the top of every page */
@@ -85,8 +60,9 @@ export const HomePage = pure(() => (
{({ measureRef, windowMeasurement: { height: windowHeight = 0 } }) => (
-
-
+
+
+
{({ browserFields, indexPattern }) => (
@@ -111,90 +87,59 @@ export const HomePage = pure(() => (
/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- />
- (
-
- )}
- />
- (
-
- )}
- />
- }
- />
-
- (
-
- )}
- />
- (
-
- )}
- />
-
-
-
+
+
+ }
+ />
+ (
+
+ )}
+ />
+ (
+
+ )}
+ />
+ (
+
+ )}
+ />
+ }
+ />
+
+ (
+
+ )}
+ />
+ (
+
+ )}
+ />
+
+
)}
-
+
+
+
+
)}
));
-
HomePage.displayName = 'HomePage';
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/translations.ts b/x-pack/legacy/plugins/siem/public/pages/home/translations.ts
index 30725828ac5eb..b87ea1c17a117 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/translations.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/home/translations.ts
@@ -18,6 +18,10 @@ export const NETWORK = i18n.translate('xpack.siem.navigation.network', {
defaultMessage: 'Network',
});
+export const DETECTION_ENGINE = i18n.translate('xpack.siem.navigation.detectionEngine', {
+ defaultMessage: 'Detection engine',
+});
+
export const TIMELINES = i18n.translate('xpack.siem.navigation.timelines', {
defaultMessage: 'Timelines',
});
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/types.ts b/x-pack/legacy/plugins/siem/public/pages/home/types.ts
index 4adf4485d8e29..101c6a69b08d1 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/types.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/home/types.ts
@@ -10,6 +10,7 @@ export enum SiemPageName {
overview = 'overview',
hosts = 'hosts',
network = 'network',
+ detectionEngine = 'detection-engine',
timelines = 'timelines',
}
@@ -17,6 +18,7 @@ export type SiemNavTabKey =
| SiemPageName.overview
| SiemPageName.hosts
| SiemPageName.network
+ | SiemPageName.detectionEngine
| SiemPageName.timelines;
export type SiemNavTab = Record;
diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx
index 738c0771ffc32..d30665c5a2142 100644
--- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx
@@ -6,39 +6,40 @@
import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui';
import React, { useContext, useEffect } from 'react';
-import { compose } from 'redux';
import { connect } from 'react-redux';
import { StickyContainer } from 'react-sticky';
+import { compose } from 'redux';
-import { inputsSelectors, State } from '../../../store';
import { FiltersGlobal } from '../../../components/filters_global';
import { HeaderPage } from '../../../components/header_page';
-import { KpiHostDetailsQuery } from '../../../containers/kpi_host_details';
import { LastEventTime } from '../../../components/last_event_time';
+import { AnomalyTableProvider } from '../../../components/ml/anomaly/anomaly_table_provider';
import { hostToCriteria } from '../../../components/ml/criteria/host_to_criteria';
-import { MlCapabilitiesContext } from '../../../components/ml/permissions/ml_capabilities_provider';
import { hasMlUserPermissions } from '../../../components/ml/permissions/has_ml_user_permissions';
-import { AnomalyTableProvider } from '../../../components/ml/anomaly/anomaly_table_provider';
+import { MlCapabilitiesContext } from '../../../components/ml/permissions/ml_capabilities_provider';
import { scoreIntervalToDateTime } from '../../../components/ml/score/score_interval_to_datetime';
-import { setHostDetailsTablesActivePageToZero as dispatchHostDetailsTablesActivePageToZero } from '../../../store/hosts/actions';
import { SiemNavigation } from '../../../components/navigation';
-import { manageQuery } from '../../../components/page/manage_query';
-import { HostOverview } from '../../../components/page/hosts/host_overview';
import { KpiHostsComponent } from '../../../components/page/hosts';
+import { HostOverview } from '../../../components/page/hosts/host_overview';
+import { manageQuery } from '../../../components/page/manage_query';
import { SiemSearchBar } from '../../../components/search_bar';
+import { WrapperPage } from '../../../components/wrapper_page';
import { HostOverviewByNameQuery } from '../../../containers/hosts/overview';
+import { KpiHostDetailsQuery } from '../../../containers/kpi_host_details';
import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../containers/source';
import { LastEventIndexKey } from '../../../graphql/types';
+import { useKibanaCore } from '../../../lib/compose/kibana_core';
import { convertToBuildEsQuery } from '../../../lib/keury';
+import { inputsSelectors, State } from '../../../store';
+import { setHostDetailsTablesActivePageToZero as dispatchHostDetailsTablesActivePageToZero } from '../../../store/hosts/actions';
import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '../../../store/inputs/actions';
import { SpyRoute } from '../../../utils/route/spy_routes';
-import { useKibanaCore } from '../../../lib/compose/kibana_core';
import { esQuery } from '../../../../../../../../src/plugins/data/public';
import { HostsEmptyPage } from '../hosts_empty_page';
+import { HostDetailsTabs } from './details_tabs';
import { navTabsHostDetails } from './nav_tabs';
import { HostDetailsComponentProps, HostDetailsProps } from './types';
-import { HostDetailsTabs } from './details_tabs';
import { type } from './utils';
const HostOverviewManage = manageQuery(HostOverview);
@@ -63,6 +64,7 @@ const HostDetailsComponent = React.memo(
}, [detailName]);
const capabilities = useContext(MlCapabilitiesContext);
const core = useKibanaCore();
+
return (
<>
@@ -96,14 +98,16 @@ const HostDetailsComponent = React.memo(
...filters,
],
});
+
return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
- <>
-
-
-
-
+
+
+
+
+
(
}
title={detailName}
/>
+
(
-
-
- >
+
+
+
+
) : (
- <>
-
+
+
+
- >
+
);
}}
+
>
);
}
);
-
HostDetailsComponent.displayName = 'HostDetailsComponent';
export const makeMapStateToProps = () => {
diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx
index a149b5224bd67..4ff666464404e 100644
--- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx
@@ -6,35 +6,35 @@
import { EuiSpacer } from '@elastic/eui';
import * as React from 'react';
-import { compose } from 'redux';
import { connect } from 'react-redux';
import { StickyContainer } from 'react-sticky';
+import { compose } from 'redux';
import { FiltersGlobal } from '../../components/filters_global';
-import { GlobalTimeArgs } from '../../containers/global_time';
import { HeaderPage } from '../../components/header_page';
-import { KpiHostsQuery } from '../../containers/kpi_hosts';
import { LastEventTime } from '../../components/last_event_time';
+import { hasMlUserPermissions } from '../../components/ml/permissions/has_ml_user_permissions';
+import { MlCapabilitiesContext } from '../../components/ml/permissions/ml_capabilities_provider';
import { SiemNavigation } from '../../components/navigation';
import { KpiHostsComponent } from '../../components/page/hosts';
import { manageQuery } from '../../components/page/manage_query';
-import { hasMlUserPermissions } from '../../components/ml/permissions/has_ml_user_permissions';
-import { MlCapabilitiesContext } from '../../components/ml/permissions/ml_capabilities_provider';
import { SiemSearchBar } from '../../components/search_bar';
+import { WrapperPage } from '../../components/wrapper_page';
+import { GlobalTimeArgs } from '../../containers/global_time';
+import { KpiHostsQuery } from '../../containers/kpi_hosts';
import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source';
import { LastEventIndexKey } from '../../graphql/types';
+import { useKibanaCore } from '../../lib/compose/kibana_core';
import { convertToBuildEsQuery } from '../../lib/keury';
import { inputsSelectors, State, hostsModel } from '../../store';
import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions';
import { SpyRoute } from '../../utils/route/spy_routes';
-import { useKibanaCore } from '../../lib/compose/kibana_core';
-
import { esQuery } from '../../../../../../../src/plugins/data/public';
import { HostsEmptyPage } from './hosts_empty_page';
+import { HostsTabs } from './hosts_tabs';
import { navTabsHosts } from './nav_tabs';
import * as i18n from './translations';
import { HostsComponentProps, HostsComponentReduxProps } from './types';
-import { HostsTabs } from './hosts_tabs';
const KpiHostsComponentManage = manageQuery(KpiHostsComponent);
@@ -64,76 +64,77 @@ const HostsComponent = React.memo(
filters,
});
return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
- <>
-
-
-
-
+
+
+
+
+
}
title={i18n.PAGE_TITLE}
/>
- <>
-
- {({ kpiHosts, loading, id, inspect, refetch }) => (
- {
- setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
- }}
- />
- )}
-
-
-
-
- >
-
-
- >
+
+
+ {({ kpiHosts, loading, id, inspect, refetch }) => (
+ {
+ setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
+ }}
+ />
+ )}
+
+
+
+
+
+
+
+
+
+
+
) : (
- <>
-
+
+
+
- >
+
);
}}
+
>
);
}
);
-
HostsComponent.displayName = 'HostsComponent';
const makeMapStateToProps = () => {
diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx
index 174b6fc5ad5d1..a059c4beeaa28 100644
--- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx
@@ -163,7 +163,7 @@ describe('Ip Details', () => {
wrapper.update();
expect(
wrapper
- .find('[data-test-subj="ip-details-headline"] [data-test-subj="page_headline_title"]')
+ .find('[data-test-subj="ip-details-headline"] [data-test-subj="header-page-title"]')
.text()
).toEqual('fe80::24ce:f7ff:fede:a571');
});
diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx
index 66a96e71808ef..96111f0479938 100644
--- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx
@@ -20,9 +20,11 @@ import { manageQuery } from '../../../components/page/manage_query';
import { FlowTargetSelectConnected } from '../../../components/page/network/flow_target_select_connected';
import { IpOverview } from '../../../components/page/network/ip_overview';
import { SiemSearchBar } from '../../../components/search_bar';
+import { WrapperPage } from '../../../components/wrapper_page';
import { IpOverviewQuery } from '../../../containers/ip_overview';
import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../containers/source';
import { FlowTargetSourceDest, LastEventIndexKey } from '../../../graphql/types';
+import { useKibanaCore } from '../../../lib/compose/kibana_core';
import { decodeIpv6 } from '../../../lib/helpers';
import { convertToBuildEsQuery } from '../../../lib/keury';
import { ConditionalFlexGroup } from '../../../pages/network/navigation/conditional_flex_group';
@@ -31,16 +33,15 @@ import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '.
import { setIpDetailsTablesActivePageToZero as dispatchIpDetailsTablesActivePageToZero } from '../../../store/network/actions';
import { SpyRoute } from '../../../utils/route/spy_routes';
import { NetworkEmptyPage } from '../network_empty_page';
-
-import { IPDetailsComponentProps } from './types';
-export { getBreadcrumbs } from './utils';
-import { TlsQueryTable } from './tls_query_table';
-import { UsersQueryTable } from './users_query_table';
-import { NetworkTopNFlowQueryTable } from './network_top_n_flow_query_table';
import { NetworkHttpQueryTable } from './network_http_query_table';
import { NetworkTopCountriesQueryTable } from './network_top_countries_query_table';
+import { NetworkTopNFlowQueryTable } from './network_top_n_flow_query_table';
+import { TlsQueryTable } from './tls_query_table';
+import { IPDetailsComponentProps } from './types';
+import { UsersQueryTable } from './users_query_table';
import { esQuery } from '../../../../../../../../src/plugins/data/public';
-import { useKibanaCore } from '../../../lib/compose/kibana_core';
+
+export { getBreadcrumbs } from './utils';
const IpOverviewManage = manageQuery(IpOverview);
@@ -85,193 +86,197 @@ export const IPDetailsComponent = React.memo(
queries: [query],
filters,
});
+
return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
- }
- title={ip}
- draggableArguments={{ field: `${flowTarget}.ip`, value: ip }}
- >
-
-
+
+ }
+ title={ip}
+ >
+
+
-
- {({ id, inspect, ipOverviewData, loading, refetch }) => (
-
- {({ isLoadingAnomaliesData, anomaliesData }) => (
- {
- const fromTo = scoreIntervalToDateTime(score, interval);
- setAbsoluteRangeDatePicker({
- id: 'global',
- from: fromTo.from,
- to: fromTo.to,
- });
- }}
- />
- )}
-
- )}
-
+
+ {({ id, inspect, ipOverviewData, loading, refetch }) => (
+
+ {({ isLoadingAnomaliesData, anomaliesData }) => (
+ {
+ const fromTo = scoreIntervalToDateTime(score, interval);
+ setAbsoluteRangeDatePicker({
+ id: 'global',
+ from: fromTo.from,
+ to: fromTo.to,
+ });
+ }}
+ />
+ )}
+
+ )}
+
-
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
) : (
- <>
-
+
+
- >
+
);
}}
+
>
);
}
);
-
IPDetailsComponent.displayName = 'IPDetailsComponent';
const makeMapStateToProps = () => {
diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx
index 33e901d5dbac8..aa7572e5741bd 100644
--- a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx
@@ -17,6 +17,7 @@ import { SiemNavigation } from '../../components/navigation';
import { manageQuery } from '../../components/page/manage_query';
import { KpiNetworkComponent } from '../../components/page/network';
import { SiemSearchBar } from '../../components/search_bar';
+import { WrapperPage } from '../../components/wrapper_page';
import { KpiNetworkQuery } from '../../containers/kpi_network';
import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source';
import { LastEventIndexKey } from '../../graphql/types';
@@ -48,6 +49,7 @@ const NetworkComponent = React.memo(
capabilitiesFetched,
}) => {
const core = useKibanaCore();
+
return (
<>
@@ -58,95 +60,95 @@ const NetworkComponent = React.memo(
queries: [query],
filters,
});
+
return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
- }
- title={i18n.PAGE_TITLE}
- />
-
-
-
-
-
-
- {({ kpiNetwork, loading, id, inspect, refetch }) => (
- {
- setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
- }}
- />
+
+ }
+ title={i18n.PAGE_TITLE}
+ />
+
+
+
+
+
+
+ {({ kpiNetwork, loading, id, inspect, refetch }) => (
+ {
+ setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
+ }}
+ />
+ )}
+
+
+ {capabilitiesFetched && !isInitializing ? (
+ <>
+
+
+
+
+
+
+
+ >
+ ) : (
+
)}
-
-
- {capabilitiesFetched && !isInitializing ? (
- <>
-
-
-
-
-
-
-
- >
- ) : (
-
- )}
-
-
+
+
+
) : (
- <>
-
+
+
- >
+
);
}}
+
>
);
}
);
-
NetworkComponent.displayName = 'NetworkComponent';
const makeMapStateToProps = () => {
diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx
index d8965f4d49491..de976b1a5c5a3 100644
--- a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx
@@ -7,68 +7,72 @@
import { EuiFlexGroup } from '@elastic/eui';
import moment from 'moment';
import React from 'react';
-import { pure } from 'recompose';
import chrome from 'ui/chrome';
import { documentationLinks } from 'ui/documentation_links';
+import { EmptyPage } from '../../components/empty_page';
import { HeaderPage } from '../../components/header_page';
import { OverviewHost } from '../../components/page/overview/overview_host';
import { OverviewNetwork } from '../../components/page/overview/overview_network';
+import { WrapperPage } from '../../components/wrapper_page';
import { GlobalTime } from '../../containers/global_time';
-
-import { Summary } from './summary';
-import { EmptyPage } from '../../components/empty_page';
import { WithSource, indicesExistOrDataTemporarilyUnavailable } from '../../containers/source';
import { SpyRoute } from '../../utils/route/spy_routes';
-
+import { Summary } from './summary';
import * as i18n from './translations';
const basePath = chrome.getBasePath();
-export const OverviewComponent = pure(() => {
+export const OverviewComponent = React.memo(() => {
const dateEnd = Date.now();
const dateRange = moment.duration(24, 'hours').asMilliseconds();
const dateStart = dateEnd - dateRange;
return (
<>
-
+
+
+
+
+ {({ indicesExist }) =>
+ indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
+
+ {({ setQuery }) => (
+
+
+
+
+
+ )}
+
+ ) : (
+
+ )
+ }
+
+
-
- {({ indicesExist }) =>
- indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
-
- {({ setQuery }) => (
-
-
-
-
-
- )}
-
- ) : (
-
- )
- }
-
>
);
});
-
OverviewComponent.displayName = 'OverviewComponent';
diff --git a/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx
index 90eae605de4b7..93c8397e21431 100644
--- a/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx
@@ -10,14 +10,13 @@ import styled from 'styled-components';
import { HeaderPage } from '../../components/header_page';
import { StatefulOpenTimeline } from '../../components/open_timeline';
+import { WrapperPage } from '../../components/wrapper_page';
import { SpyRoute } from '../../utils/route/spy_routes';
-
import * as i18n from './translations';
const TimelinesContainer = styled.div`
width: 100%:
`;
-
TimelinesContainer.displayName = 'TimelinesContainer';
interface TimelinesProps {
@@ -30,16 +29,19 @@ export const DEFAULT_SEARCH_RESULTS_PER_PAGE = 10;
export const TimelinesPage = React.memo(({ apolloClient }) => (
<>
-
-
-
-
-
+
+
+
+
+
+
+
+
>
));
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 645b8c427dc15..a5c950a3f2be0 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -10511,7 +10511,6 @@
"xpack.siem.formatted.duration.noDurationTooltip": "期間がありません",
"xpack.siem.formatted.duration.zeroNanosecondsTooltip": "0ナノ秒",
"xpack.siem.formattedDuration.tooltipLabel": "生",
- "xpack.siem.global.addData": "データの投入",
"xpack.siem.headerPage.pageSubtitle": "前回のイベント: {beat}",
"xpack.siem.host.details.architectureLabel": "アーキテクチャー",
"xpack.siem.host.details.firstSeenTitle": "初回の認識",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index d542e66b247ec..bb876ca1eb8b9 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -10601,7 +10601,6 @@
"xpack.siem.formatted.duration.noDurationTooltip": "无持续时间",
"xpack.siem.formatted.duration.zeroNanosecondsTooltip": "零纳秒",
"xpack.siem.formattedDuration.tooltipLabel": "原始",
- "xpack.siem.global.addData": "添加数据",
"xpack.siem.headerPage.pageSubtitle": "最后事件:{beat}",
"xpack.siem.host.details.architectureLabel": "架构",
"xpack.siem.host.details.firstSeenTitle": "首次看到时间",