From 9cfd404c4d815336ae55c335f81857bfbec01a48 Mon Sep 17 00:00:00 2001 From: parkiino Date: Mon, 18 Nov 2024 12:33:22 -0500 Subject: [PATCH 1/7] working banner w/ correct design --- packages/kbn-doc-links/src/get_doc_links.ts | 1 + packages/kbn-doc-links/src/types.ts | 1 + .../bidirectional_integrations_callout.tsx | 66 +++++++++++++++++++ .../epm/screens/detail/components/index.tsx | 2 + .../epm/screens/detail/overview/overview.tsx | 25 ++++++- 5 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.tsx diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 251d08dde715a..52a3e7b7427e7 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -470,6 +470,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D securitySolution: { artifactControl: `${SECURITY_SOLUTION_DOCS}artifact-control.html`, avcResults: `${ELASTIC_WEBSITE_URL}blog/elastic-av-comparatives-business-security-test`, + bidirectionalIntegrations: `${SECURITY_SOLUTION_DOCS}third-party-actions.html`, trustedApps: `${SECURITY_SOLUTION_DOCS}trusted-apps-ov.html`, eventFilters: `${SECURITY_SOLUTION_DOCS}event-filters.html`, blocklist: `${SECURITY_SOLUTION_DOCS}blocklist.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index f1a6a8d4b578d..d3ece5b61daa1 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -340,6 +340,7 @@ export interface DocLinks { readonly aiAssistant: string; readonly artifactControl: string; readonly avcResults: string; + readonly bidirectionalIntegrations: string; readonly trustedApps: string; readonly eventFilters: string; readonly eventMerging: string; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.tsx new file mode 100644 index 0000000000000..cbafad70b632e --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import styled from 'styled-components'; +import { EuiCallOut, EuiLink, EuiTextColor } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; + +const AccentCallout = styled(EuiCallOut)` + .euiCallOutHeader__title { + color: ${(props) => props.theme.eui.euiColorAccent}; + } + background-color: ${(props) => props.theme.eui.euiPanelBackgroundColorModifiers.accent}; +`; + +export interface BidirectionalIntegrationsBannerProps { + onDismiss: () => void; +} +export const BidirectionalIntegrationsBanner = memo( + ({ onDismiss }) => { + const { docLinks } = useKibana().services; + + const bannerTitle = ( + + + + ); + + return ( + + + + + ), + }} + /> + + ); + } +); +BidirectionalIntegrationsBanner.displayName = 'BidirectionalIntegrationsBanner'; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/index.tsx index 4aa1a543897c9..cb1fc1b396e42 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/index.tsx @@ -6,7 +6,9 @@ */ export { BackLink } from './back_link'; export { AddIntegrationButton } from './add_integration_button'; +export { CloudPostureThirdPartySupportCallout } from './cloud_posture_third_party_support_callout'; export { UpdateIcon } from './update_icon'; export { IntegrationAgentPolicyCount } from './integration_agent_policy_count'; export { IconPanel, LoadingIconPanel } from './icon_panel'; export { KeepPoliciesUpToDateSwitch } from './keep_policies_up_to_date_switch'; +export { BidirectionalIntegrationsBanner } from './bidirectional_integrations_callout'; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx index e96e74b1bb96c..8bf6146dc0f9a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx @@ -42,7 +42,10 @@ import { SideBarColumn } from '../../../components/side_bar_column'; import type { FleetStartServices } from '../../../../../../../plugin'; -import { CloudPostureThirdPartySupportCallout } from '../components/cloud_posture_third_party_support_callout'; +import { + CloudPostureThirdPartySupportCallout, + BidirectionalIntegrationsBanner, +} from '../components'; import { Screenshots } from './screenshots'; import { Readme } from './readme'; @@ -172,6 +175,8 @@ export const OverviewPage: React.FC = memo( const isUnverified = isPackageUnverified(packageInfo, packageVerificationKeyId); const isPrerelease = isPackagePrerelease(packageInfo.version); const isElasticDefend = packageInfo.name === 'endpoint'; + const isSentinelOneCloudFunnel = packageInfo.name === 'sentinel_one_cloud_funnel'; + const isCrowdStrike = packageInfo.name === 'crowdstrike'; const [markdown, setMarkdown] = useState(undefined); const [selectedItemId, setSelectedItem] = useState(undefined); const [isSideNavOpenOnMobile, setIsSideNavOpenOnMobile] = useState(false); @@ -296,11 +301,19 @@ export const OverviewPage: React.FC = memo( const [showAVCBanner, setShowAVCBanner] = useState( storage.get('securitySolution.showAvcBanner') ?? true ); - const onBannerDismiss = useCallback(() => { + const [showBidIntBanner, setShowBidIntBanner] = useState( + storage.get('securitySolution.showBidIntBanner') ?? true + ); + const onAVCBannerDismiss = useCallback(() => { setShowAVCBanner(false); storage.set('securitySolution.showAvcBanner', false); }, [storage]); + const onBidIntBannerDismiss = useCallback(() => { + setShowBidIntBanner(false); + storage.set('securitySolution.showBidIntBanner', false); + }, [storage]); + return ( @@ -317,7 +330,13 @@ export const OverviewPage: React.FC = memo( {isUnverified && } {useIsStillYear2024() && isElasticDefend && showAVCBanner && ( <> - + + + + )} + {(isCrowdStrike || isSentinelOneCloudFunnel) && showBidIntBanner && ( + <> + )} From d605d6017c92baae99677d08d253b5bb46abdb1b Mon Sep 17 00:00:00 2001 From: parkiino Date: Mon, 18 Nov 2024 13:26:28 -0500 Subject: [PATCH 2/7] fix i18n --- .../detail/components/bidirectional_integrations_callout.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.tsx index cbafad70b632e..5f8375d2e7baa 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.tsx @@ -28,7 +28,7 @@ export const BidirectionalIntegrationsBanner = memo @@ -42,7 +42,7 @@ export const BidirectionalIntegrationsBanner = memo Date: Mon, 18 Nov 2024 14:49:27 -0500 Subject: [PATCH 3/7] separate dismissal for cs and sentinel one --- .../epm/screens/detail/overview/overview.tsx | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx index 8bf6146dc0f9a..12260724d7395 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx @@ -301,17 +301,25 @@ export const OverviewPage: React.FC = memo( const [showAVCBanner, setShowAVCBanner] = useState( storage.get('securitySolution.showAvcBanner') ?? true ); - const [showBidIntBanner, setShowBidIntBanner] = useState( - storage.get('securitySolution.showBidIntBanner') ?? true + const [showCSBidIntBanner, setShowCSBidIntBanner] = useState( + storage.get('fleet.showBidIntBannerForCS') ?? true + ); + const [showSOCFBidIntBanner, setShowSOCFBidIntBanner] = useState( + storage.get('fleet.showBidIntBannerForSOCF') ?? true ); const onAVCBannerDismiss = useCallback(() => { setShowAVCBanner(false); storage.set('securitySolution.showAvcBanner', false); }, [storage]); - const onBidIntBannerDismiss = useCallback(() => { - setShowBidIntBanner(false); - storage.set('securitySolution.showBidIntBanner', false); + const onCSBidIntBannerDismiss = useCallback(() => { + setShowCSBidIntBanner(false); + storage.set('fleet.showBidIntBannerForCS', false); + }, [storage]); + + const onSOCFBidIntBannerDismiss = useCallback(() => { + setShowSOFCBidIntBanner(false); + storage.set('fleet.showBidIntBannerForSOCF', false); }, [storage]); return ( @@ -334,9 +342,15 @@ export const OverviewPage: React.FC = memo( )} - {(isCrowdStrike || isSentinelOneCloudFunnel) && showBidIntBanner && ( + {isCrowdStrike && showCSBidIntBanner && ( + <> + + + + )} + {isSentinelOneCloudFunnel && showSOCFBidIntBanner && ( <> - + )} From b91f700b0937e284a70705279b1884e59b6b3d4a Mon Sep 17 00:00:00 2001 From: parkiino Date: Mon, 18 Nov 2024 15:21:49 -0500 Subject: [PATCH 4/7] rename more descriptive names --- .../epm/screens/detail/overview/overview.tsx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx index 12260724d7395..9d7e27a47adb9 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx @@ -301,25 +301,25 @@ export const OverviewPage: React.FC = memo( const [showAVCBanner, setShowAVCBanner] = useState( storage.get('securitySolution.showAvcBanner') ?? true ); - const [showCSBidIntBanner, setShowCSBidIntBanner] = useState( - storage.get('fleet.showBidIntBannerForCS') ?? true + const [showCSResponseSupportBanner, setShowCSResponseSupportBanner] = useState( + storage.get('fleet.showCSResponseSupportBanner') ?? true ); - const [showSOCFBidIntBanner, setShowSOCFBidIntBanner] = useState( - storage.get('fleet.showBidIntBannerForSOCF') ?? true + const [showSOReponseSupportBanner, setShowSOResponseSupportBanner] = useState( + storage.get('fleet.showSOReponseSupportBanner') ?? true ); const onAVCBannerDismiss = useCallback(() => { setShowAVCBanner(false); storage.set('securitySolution.showAvcBanner', false); }, [storage]); - const onCSBidIntBannerDismiss = useCallback(() => { - setShowCSBidIntBanner(false); - storage.set('fleet.showBidIntBannerForCS', false); + const onCSResponseSupportBannerDismiss = useCallback(() => { + setShowCSResponseSupportBanner(false); + storage.set('fleet.showCSResponseSupportBanner', false); }, [storage]); - const onSOCFBidIntBannerDismiss = useCallback(() => { - setShowSOFCBidIntBanner(false); - storage.set('fleet.showBidIntBannerForSOCF', false); + const onSOResponseSupportBannerDismiss = useCallback(() => { + setShowSOResponseSupportBanner(false); + storage.set('fleet.showSOReponseSupportBanner', false); }, [storage]); return ( @@ -342,15 +342,15 @@ export const OverviewPage: React.FC = memo( )} - {isCrowdStrike && showCSBidIntBanner && ( + {isCrowdStrike && showCSResponseSupportBanner && ( <> - + )} - {isSentinelOneCloudFunnel && showSOCFBidIntBanner && ( + {isSentinelOneCloudFunnel && showSOReponseSupportBanner && ( <> - + )} From 9341c374408c584e3d9281453008e99cc48b642f Mon Sep 17 00:00:00 2001 From: parkiino Date: Mon, 18 Nov 2024 15:23:51 -0500 Subject: [PATCH 5/7] change from sentinel one cloud funnel to just sentinel one --- .../sections/epm/screens/detail/overview/overview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx index 9d7e27a47adb9..83cde5745071a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx @@ -175,7 +175,7 @@ export const OverviewPage: React.FC = memo( const isUnverified = isPackageUnverified(packageInfo, packageVerificationKeyId); const isPrerelease = isPackagePrerelease(packageInfo.version); const isElasticDefend = packageInfo.name === 'endpoint'; - const isSentinelOneCloudFunnel = packageInfo.name === 'sentinel_one_cloud_funnel'; + const isSentinelOne = packageInfo.name === 'sentinel_one'; const isCrowdStrike = packageInfo.name === 'crowdstrike'; const [markdown, setMarkdown] = useState(undefined); const [selectedItemId, setSelectedItem] = useState(undefined); @@ -348,7 +348,7 @@ export const OverviewPage: React.FC = memo( )} - {isSentinelOneCloudFunnel && showSOReponseSupportBanner && ( + {isSentinelOne && showSOReponseSupportBanner && ( <> From 63ff4ed3209494f445efb7c2fa559597ac4cd7bf Mon Sep 17 00:00:00 2001 From: parkiino Date: Mon, 18 Nov 2024 15:32:13 -0500 Subject: [PATCH 6/7] unit test wip --- ...idirectional_integrations_callout.test.tsx | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.test.tsx diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.test.tsx new file mode 100644 index 0000000000000..75b726ea377eb --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.test.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, type RenderResult } from '@testing-library/react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { ThemeProvider } from 'styled-components'; + +import { + BidirectionalIntegrationsBanner, + type BidirectionalIntegrationsBannerProps, +} from './bidirectional_integrations_callout'; + +jest.mock('react-use/lib/useLocalStorage'); + +describe('BidirectionalIntegrationsBanner', () => { + let formProps: BidirectionalIntegrationsBannerProps; + let renderResult: RenderResult; + + beforeEach(() => { + formProps = { + onDismiss: jest.fn(), + }; + + renderResult = render( + + ({ + eui: { + euiColorAccent: '#f04e98', + euiPanelBackgroundColorModifiers: { accent: '#feedf5' }, + }, + })} + > + + + + ); + }); + + it('should render bidirectional integrations banner', () => { + expect(renderResult.getByTestId('bidirectionalIntegrationsCallout')).toBeInTheDocument(); + }); + + it('should contain a link to documentation', () => { + const docLink = renderResult.getByTestId('bidirectionalIntegrationDocLink'); + + expect(docLink).toBeInTheDocument(); + expect(docLink.getAttribute('href')).toContain('third-party-actions.html'); + }); + + it('should call `onDismiss` callback when user clicks dismiss', () => { + renderResult.getByTestId('euiDismissCalloutButton').click(); + + expect(formProps.onDismiss).toBeCalled(); + }); +}); From b6da22a3de6616c589ab02b1ce3e15e97bc7b053 Mon Sep 17 00:00:00 2001 From: parkiino Date: Mon, 18 Nov 2024 16:06:12 -0500 Subject: [PATCH 7/7] fixed unit test --- ...idirectional_integrations_callout.test.tsx | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.test.tsx index 75b726ea377eb..6dd9ff0e5f9b3 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/bidirectional_integrations_callout.test.tsx @@ -6,9 +6,9 @@ */ import React from 'react'; -import { render, type RenderResult } from '@testing-library/react'; -import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; -import { ThemeProvider } from 'styled-components'; +import { type RenderResult } from '@testing-library/react'; + +import { createFleetTestRendererMock } from '../../../../../../../mock'; import { BidirectionalIntegrationsBanner, @@ -26,20 +26,9 @@ describe('BidirectionalIntegrationsBanner', () => { onDismiss: jest.fn(), }; - renderResult = render( - - ({ - eui: { - euiColorAccent: '#f04e98', - euiPanelBackgroundColorModifiers: { accent: '#feedf5' }, - }, - })} - > - - - - ); + const renderer = createFleetTestRendererMock(); + + renderResult = renderer.render(); }); it('should render bidirectional integrations banner', () => {