diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embeddable.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embeddable.test.tsx.snap new file mode 100644 index 000000000000..f343316d88c4 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embeddable.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Embeddable it renders 1`] = ` + + +

+ Test content +

+
+
+`; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embeddable_header.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embeddable_header.test.tsx.snap new file mode 100644 index 000000000000..e88693b292a5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embeddable_header.test.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EmbeddableHeader it renders 1`] = ` + + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embedded_map.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embedded_map.test.tsx.snap index 1f23686adca0..bf0dfd941787 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embedded_map.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/embedded_map.test.tsx.snap @@ -1,19 +1,34 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`EmbeddedMap renders correctly against snapshot 1`] = ` - + + + + + Map configuration help + + + } > - + - - - + + `; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/index_patterns_missing_prompt.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/index_patterns_missing_prompt.test.tsx.snap index 1aa3b087ac0d..fb896059460b 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/index_patterns_missing_prompt.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/__snapshots__/index_patterns_missing_prompt.test.tsx.snap @@ -15,43 +15,41 @@ exports[`IndexPatternsMissingPrompt renders correctly against snapshot 1`] = ` body={

- An ECS compliant Kibana index pattern must be configured to view event data on the map. When using beats, you can run the following setup commands to create the required Kibana index patterns, otherwise you can configure them manually within Kibana settings. + + beats + , + "example": + ./packetbeat setup + , + "setup": + setup + , + } + } + />

- - auditbeat-* - - , - - filebeat-* - - , - - packetbeat-* - - , - - winlogbeat-* - +

} iconType="gisApp" title={

- Required Index Patterns Not Configured + Required index patterns not configured

} titleSize="xs" diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable.test.tsx new file mode 100644 index 000000000000..49f5306dc1b6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable.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 { Embeddable } from './embeddable'; + +jest.mock('../../lib/settings/use_kibana_ui_setting'); + +describe('Embeddable', () => { + test('it renders', () => { + const wrapper = shallow( + + +

{'Test content'}

+
+
+ ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable.tsx new file mode 100644 index 000000000000..b9a2d01ee0a7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable.tsx @@ -0,0 +1,25 @@ +/* + * 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 { EuiPanel } from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; + +const Panel = styled(EuiPanel)` + overflow: hidden; +`; +Panel.displayName = 'Panel'; + +export interface EmbeddableProps { + children: React.ReactNode; +} + +export const Embeddable = React.memo(({ children }) => ( +
+ {children} +
+)); +Embeddable.displayName = 'Embeddable'; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable_header.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable_header.test.tsx new file mode 100644 index 000000000000..4536da3ba7b9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable_header.test.tsx @@ -0,0 +1,74 @@ +/* + * 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 { EmbeddableHeader } from './embeddable_header'; + +jest.mock('../../lib/settings/use_kibana_ui_setting'); + +describe('EmbeddableHeader', () => { + test('it renders', () => { + const wrapper = shallow( + + + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + test('it renders the title', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="header-embeddable-title"]') + .first() + .exists() + ).toBe(true); + }); + + test('it renders supplements when children provided', () => { + const wrapper = mount( + + +

{'Test children'}

+
+
+ ); + + expect( + wrapper + .find('[data-test-subj="header-embeddable-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-embeddable-supplements"]') + .first() + .exists() + ).toBe(false); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable_header.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable_header.tsx new file mode 100644 index 000000000000..dbd9e3f763f9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embeddable_header.tsx @@ -0,0 +1,43 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import React from 'react'; +import styled, { css } from 'styled-components'; + +const Header = styled.header.attrs({ + className: 'siemEmbeddable__header', +})` + ${({ theme }) => css` + border-bottom: ${theme.eui.euiBorderThin}; + padding: ${theme.eui.paddingSizes.m}; + `} +`; +Header.displayName = 'Header'; + +export interface EmbeddableHeaderProps { + children?: React.ReactNode; + title: string | React.ReactNode; +} + +export const EmbeddableHeader = React.memo(({ children, title }) => ( +
+ + + +
{title}
+
+
+ + {children && ( + + {children} + + )} +
+
+)); +EmbeddableHeader.displayName = 'EmbeddableHeader'; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx index 87e38e0fac2e..1c712f874969 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx @@ -4,41 +4,74 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiSpacer } from '@elastic/eui'; +import { EuiLink, EuiText } from '@elastic/eui'; import { Filter } from '@kbn/es-query'; import React, { useEffect, useState } from 'react'; -import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; import { createPortalNode, InPortal } from 'react-reverse-portal'; import { Query } from 'src/plugins/data/common'; +import styled, { css } from 'styled-components'; +import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; -import styled from 'styled-components'; -import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { EmbeddablePanel } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; - +import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; +import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { getIndexPatternTitleIdMapping } from '../../hooks/api/helpers'; import { useIndexPatterns } from '../../hooks/use_index_patterns'; -import { useKibanaUiSetting } from '../../lib/settings/use_kibana_ui_setting'; -import { DEFAULT_INDEX_KEY } from '../../../common/constants'; -import { useKibanaPlugins } from '../../lib/compose/kibana_plugins'; import { useKibanaCore } from '../../lib/compose/kibana_core'; -import { useStateToaster } from '../toasters'; +import { useKibanaPlugins } from '../../lib/compose/kibana_plugins'; +import { useKibanaUiSetting } from '../../lib/settings/use_kibana_ui_setting'; import { Loader } from '../loader'; -import { IndexPatternsMissingPrompt } from './index_patterns_missing_prompt'; -import { MapEmbeddable, SetQuery } from './types'; -import * as i18n from './translations'; - +import { useStateToaster } from '../toasters'; +import { Embeddable } from './embeddable'; +import { EmbeddableHeader } from './embeddable_header'; import { createEmbeddable, displayErrorToast, setupEmbeddablesAPI } from './embedded_map_helpers'; +import { IndexPatternsMissingPrompt } from './index_patterns_missing_prompt'; import { MapToolTip } from './map_tool_tip/map_tool_tip'; +import * as i18n from './translations'; +import { MapEmbeddable, SetQuery } from './types'; -const EmbeddableWrapper = styled(EuiFlexGroup)` - position: relative; - height: 400px; - margin: 0; +interface EmbeddableMapProps { + maintainRatio?: boolean; +} - .mapToolbarOverlay__button { - display: none; - } +const EmbeddableMap = styled.div.attrs({ + className: 'siemEmbeddable__map', +})` + ${({ maintainRatio, theme }) => css` + .embPanel { + border: none; + box-shadow: none; + } + + .mapToolbarOverlay__button { + display: none; + } + + ${maintainRatio && + css` + padding-top: calc(3 / 4 * 100%); //4:3 (standard) ratio + position: relative; + + @media only screen and (min-width: ${theme.eui.euiBreakpoints.m}) { + padding-top: calc(9 / 32 * 100%); //32:9 (ultra widescreen) ratio + } + + @media only screen and (min-width: 1441px) and (min-height: 901px) { + padding-top: calc(9 / 21 * 100%); //21:9 (ultrawide) ratio + } + + .embPanel { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + } + `} + `} `; +EmbeddableMap.displayName = 'EmbeddableMap'; export interface EmbeddedMapProps { query: Query; @@ -152,11 +185,23 @@ export const EmbeddedMap = React.memo( }, [startDate, endDate]); return isError ? null : ( - <> + + + + + {i18n.EMBEDDABLE_HEADER_HELP} + + + + - + + {embeddable != null ? ( ( ) : ( )} - - - + + ); } ); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/index_patterns_missing_prompt.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/index_patterns_missing_prompt.tsx index 709725b853dd..e71398455ee8 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/index_patterns_missing_prompt.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/index_patterns_missing_prompt.tsx @@ -4,69 +4,58 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; -import { EuiButton, EuiEmptyPrompt, EuiLink } from '@elastic/eui'; - +import { EuiButton, EuiCode, EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import * as React from 'react'; import chrome from 'ui/chrome'; +import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; import * as i18n from './translations'; -interface DocMapping { - beat: string; - docLink: string; -} +export const IndexPatternsMissingPrompt = React.memo(() => ( + {i18n.ERROR_TITLE}} + titleSize="xs" + body={ + <> +

+ + {'beats'} + + ), + setup: {'setup'}, + example: {'./packetbeat setup'}, + }} + /> +

-export const IndexPatternsMissingPrompt = React.memo(() => { - const beatsSetupDocMapping: DocMapping[] = [ - { - beat: 'auditbeat', - docLink: `${ELASTIC_WEBSITE_URL}/guide/en/beats/auditbeat/${DOC_LINK_VERSION}/load-kibana-dashboards.html`, - }, - { - beat: 'filebeat', - docLink: `${ELASTIC_WEBSITE_URL}/guide/en/beats/filebeat/${DOC_LINK_VERSION}/load-kibana-dashboards.html`, - }, - { - beat: 'packetbeat', - docLink: `${ELASTIC_WEBSITE_URL}/guide/en/beats/packetbeat/${DOC_LINK_VERSION}/load-kibana-dashboards.html`, - }, - { - beat: 'winlogbeat', - docLink: `${ELASTIC_WEBSITE_URL}/guide/en/beats/winlogbeat/${DOC_LINK_VERSION}/load-kibana-dashboards.html`, - }, - ]; - - return ( - {i18n.ERROR_TITLE}} - titleSize="xs" - body={ - <> -

{i18n.ERROR_DESCRIPTION}

- -

- {beatsSetupDocMapping - .map(v => ( - - {`${v.beat}-*`} - - )) - .reduce((acc, v) => [acc, ', ', v])} -

- - } - actions={ - - {i18n.ERROR_BUTTON} - - } - /> - ); -}); +

+ +

+ + } + actions={ + + {i18n.ERROR_BUTTON} + + } + /> +)); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/translations.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/translations.ts index 373bd1d054a4..958619bee19d 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/translations.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/translations.ts @@ -6,6 +6,20 @@ import { i18n } from '@kbn/i18n'; +export const EMBEDDABLE_HEADER_TITLE = i18n.translate( + 'xpack.siem.components.embeddables.embeddedMap.embeddableHeaderTitle', + { + defaultMessage: 'Network map', + } +); + +export const EMBEDDABLE_HEADER_HELP = i18n.translate( + 'xpack.siem.components.embeddables.embeddedMap.embeddableHeaderHelp', + { + defaultMessage: 'Map configuration help', + } +); + export const MAP_TITLE = i18n.translate( 'xpack.siem.components.embeddables.embeddedMap.embeddablePanelTitle', { @@ -51,15 +65,7 @@ export const ERROR_CREATING_EMBEDDABLE = i18n.translate( export const ERROR_TITLE = i18n.translate( 'xpack.siem.components.embeddables.indexPatternsMissingPrompt.errorTitle', { - defaultMessage: 'Required Index Patterns Not Configured', - } -); - -export const ERROR_DESCRIPTION = i18n.translate( - 'xpack.siem.components.embeddables.indexPatternsMissingPrompt.errorDescription', - { - defaultMessage: - 'An ECS compliant Kibana index pattern must be configured to view event data on the map. When using beats, you can run the following setup commands to create the required Kibana index patterns, otherwise you can configure them manually within Kibana settings.', + defaultMessage: 'Required index patterns not configured', } ); 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 b10c09d65426..f7b3cfb4962f 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx @@ -14,9 +14,9 @@ import { EmbeddedMap } from '../../components/embeddables/embedded_map'; import { FiltersGlobal } from '../../components/filters_global'; import { HeaderPage } from '../../components/header_page'; import { LastEventTime } from '../../components/last_event_time'; +import { SiemNavigation } from '../../components/navigation'; import { manageQuery } from '../../components/page/manage_query'; import { KpiNetworkComponent } from '../../components/page/network'; -import { SiemNavigation } from '../../components/navigation'; import { SiemSearchBar } from '../../components/search_bar'; import { KpiNetworkQuery } from '../../containers/kpi_network'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; @@ -26,7 +26,6 @@ import { convertToBuildEsQuery } from '../../lib/keury'; import { networkModel, State, inputsSelectors } from '../../store'; import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { SpyRoute } from '../../utils/route/spy_routes'; - import { navTabsNetwork, NetworkRoutes, NetworkRoutesLoading } from './navigation'; import { NetworkEmptyPage } from './network_empty_page'; import * as i18n from './translations'; @@ -78,6 +77,8 @@ const NetworkComponent = React.memo( setQuery={setQuery} /> + +