diff --git a/x-pack/plugins/security_solution/public/app/components/onboarding/onboarding_page_service.ts b/x-pack/plugins/security_solution/public/app/components/onboarding/onboarding_page_service.ts new file mode 100644 index 0000000000000..ea5beab4608d2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/app/components/onboarding/onboarding_page_service.ts @@ -0,0 +1,48 @@ +/* + * 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 type { Observable } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; +import type { SecurityProductTypes } from '../../../common/components/landing_page/onboarding/configs'; +import type { StepId } from '../../../common/components/landing_page/onboarding/types'; + +export class OnboardingPageService { + private productTypesSubject$: BehaviorSubject; + private projectsUrlSubject$: BehaviorSubject; + private projectFeaturesUrlSubject$: BehaviorSubject; + private availableStepsSubject$: BehaviorSubject; + + public productTypes$: Observable; + public projectsUrl$: Observable; + public projectFeaturesUrl$: Observable; + public availableSteps$: Observable; + + constructor() { + this.productTypesSubject$ = new BehaviorSubject(undefined); + this.projectsUrlSubject$ = new BehaviorSubject(undefined); + this.projectFeaturesUrlSubject$ = new BehaviorSubject(undefined); + this.availableStepsSubject$ = new BehaviorSubject([]); + + this.productTypes$ = this.productTypesSubject$.asObservable(); + this.projectsUrl$ = this.projectsUrlSubject$.asObservable(); + this.projectFeaturesUrl$ = this.projectFeaturesUrlSubject$.asObservable(); + this.availableSteps$ = this.availableStepsSubject$.asObservable(); + } + + setProductTypes(productTypes: SecurityProductTypes) { + this.productTypesSubject$.next(productTypes); + } + setProjectFeaturesUrl(projectFeaturesUrl: string | undefined) { + this.projectFeaturesUrlSubject$.next(projectFeaturesUrl); + } + setProjectsUrl(projectsUrl: string | undefined) { + this.projectsUrlSubject$.next(projectsUrl); + } + setAvailableSteps(availableSteps: StepId[]) { + this.availableStepsSubject$.next(availableSteps); + } +} diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/index.test.tsx index 359cb39ee001b..90d1cce4a52dd 100644 --- a/x-pack/plugins/security_solution/public/common/components/landing_page/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/index.test.tsx @@ -8,24 +8,19 @@ import React from 'react'; import { render } from '@testing-library/react'; import { LandingPageComponent } from '.'; -const mockUseContractComponents = jest.fn(() => ({})); -jest.mock('../../hooks/use_contract_component', () => ({ - useContractComponents: () => mockUseContractComponents(), -})); jest.mock('../../containers/sourcerer', () => ({ useSourcererDataView: jest.fn().mockReturnValue({ indicesExist: false }), })); +jest.mock('./onboarding'); describe('LandingPageComponent', () => { beforeEach(() => { jest.clearAllMocks(); }); - it('renders the get started component', () => { - const GetStarted = () =>
; - mockUseContractComponents.mockReturnValue({ GetStarted }); + it('renders the onboarding component', () => { const { queryByTestId } = render(); - expect(queryByTestId('get-started-mock')).toBeInTheDocument(); + expect(queryByTestId('onboarding-with-settings')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/index.tsx index d013fdd715918..e631ff53f6a51 100644 --- a/x-pack/plugins/security_solution/public/common/components/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/index.tsx @@ -5,14 +5,14 @@ * 2.0. */ -import React, { memo } from 'react'; +import React, { memo, useMemo } from 'react'; import { useSourcererDataView } from '../../containers/sourcerer'; -import { useContractComponents } from '../../hooks/use_contract_component'; +import { getOnboardingComponent } from './onboarding'; export const LandingPageComponent = memo(() => { - const { GetStarted } = useContractComponents(); const { indicesExist } = useSourcererDataView(); - return GetStarted ? : null; + const OnBoarding = useMemo(() => getOnboardingComponent(), []); + return ; }); LandingPageComponent.displayName = 'LandingPageComponent'; diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/index.tsx new file mode 100644 index 0000000000000..dc8523ef0a772 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/index.tsx @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; + +export const getOnboardingComponent = jest + .fn() + .mockReturnValue(() =>
); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/onboarding_with_settings.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/onboarding_with_settings.tsx new file mode 100644 index 0000000000000..da354ec8edcc2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/onboarding_with_settings.tsx @@ -0,0 +1,12 @@ +/* + * 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'; + +export const OnboardingWithSettingsComponent = () => ( +
+); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/product_switch.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/product_switch.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/product_switch.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/product_switch.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/storage.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/storage.ts similarity index 95% rename from x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/storage.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/storage.ts index 48e43b8fb49df..87503ddc0b779 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/storage.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/storage.ts @@ -5,7 +5,7 @@ * 2.0. */ -export const getStartedStorage = { +export const onboardingStorage = { getAllFinishedStepsFromStorage: jest.fn(() => ({})), getFinishedStepsFromStorageByCardId: jest.fn(() => []), getActiveProductsFromStorage: jest.fn(() => []), diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/toggle_panel.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/toggle_panel.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/__mocks__/toggle_panel.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/__mocks__/toggle_panel.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/apis/index.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/apis/index.ts similarity index 79% rename from x-pack/plugins/security_solution_serverless/public/get_started/apis/index.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/apis/index.ts index 0b515154f178f..7149d1ecf6c72 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/apis/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/apis/index.ts @@ -5,9 +5,9 @@ * 2.0. */ -import type { FetchRulesResponse } from '@kbn/security-solution-plugin/public'; -import { DETECTION_ENGINE_RULES_URL_FIND } from '@kbn/security-solution-plugin/common'; import type { HttpSetup } from '@kbn/core/public'; +import { DETECTION_ENGINE_RULES_URL_FIND } from '../../../../../../common/constants'; +import type { FetchRulesResponse } from '../../../../../detection_engine/rule_management/logic/types'; export const fetchRuleManagementFilters = async ({ http, diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/badge.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/badge.ts similarity index 95% rename from x-pack/plugins/security_solution_serverless/public/get_started/badge.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/badge.ts index 1743259fd7eaa..2cd0e0a20a7f8 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/badge.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/badge.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ProductLine } from '../../common/product'; +import { ProductLine } from './configs'; import { PRODUCT_BADGE_ANALYTICS, PRODUCT_BADGE_CLOUD, PRODUCT_BADGE_EDR } from './translations'; import type { Badge } from './types'; import { BadgeId } from './types'; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/badges.test.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/badges.test.ts similarity index 93% rename from x-pack/plugins/security_solution_serverless/public/get_started/badges.test.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/badges.test.ts index 95d7934a65027..6921389d67149 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/badges.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/badges.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { ProductLine } from '../../common/product'; import { analyticsBadge, cloudBadge, edrBadge, getProductBadges } from './badge'; +import { ProductLine } from './configs'; describe('getProductBadges', () => { test('should return all badges if no productLineRequired is passed', () => { diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_item.test.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_item.test.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_item.tsx similarity index 93% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_item.tsx index 535b01523c607..e7dbdfd4e318a 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_item.tsx @@ -6,7 +6,9 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; + import React, { useMemo, useCallback } from 'react'; + import classnames from 'classnames'; import type { CardId, @@ -20,6 +22,8 @@ import { getCard } from './helpers'; import { CardStep } from './card_step'; import { useCardItemStyles } from './styles/card_item.styles'; +export const SHADOW_ANIMATION_DURATION = 350; + const CardItemComponent: React.FC<{ activeStepIds: StepId[] | undefined; cardId: CardId; @@ -44,12 +48,16 @@ const CardItemComponent: React.FC<{ () => new Set(expandedCardSteps[cardId]?.expandedSteps ?? []), [cardId, expandedCardSteps] ); + const cardItemPanelStyle = useCardItemStyles(); - const cardClassNames = classnames('card-item', { - 'card-expanded': isExpandedCard, - }); + const cardClassNames = classnames( + 'card-item', + { + 'card-expanded': isExpandedCard, + }, + cardItemPanelStyle + ); - const cardItemPanelStyle = useCardItemStyles(); const getCardStep = useCallback( (stepId: StepId) => cardItem?.steps?.find((step) => step.id === stepId), [cardItem?.steps] @@ -91,7 +99,6 @@ const CardItemComponent: React.FC<{ className={cardClassNames} hasBorder paddingSize="none" - css={cardItemPanelStyle} borderRadius="none" data-test-subj={cardId} > diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/__mocks__/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/__mocks__/index.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/__mocks__/index.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/__mocks__/index.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/__mocks__/content_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/__mocks__/content_wrapper.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/__mocks__/content_wrapper.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/__mocks__/content_wrapper.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/add_integration_image.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/add_integration_image.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/add_integration_image.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/add_integration_image.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/content_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/content_wrapper.tsx similarity index 77% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/content_wrapper.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/content_wrapper.tsx index 49b4765cbeb33..7f67817d71d42 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/content_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/content_wrapper.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import classnames from 'classnames'; import React from 'react'; import { useStepContentStyles } from '../../styles/step_content.styles'; @@ -14,12 +15,8 @@ const ContentWrapperComponent: React.FC<{ children: React.ReactElement; shadow?: }) => { const { getRightContentStyles } = useStepContentStyles(); const rightContentStyles = getRightContentStyles({ shadow }); - - return ( -
- {children} -
- ); + const rightPanelContentClassNames = classnames('right-panel-content', rightContentStyles); + return
{children}
; }; export const ContentWrapper = React.memo(ContentWrapperComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/create_project_step_image.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/create_project_step_image.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/create_project_step_image.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/create_project_step_image.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/enable_rule_image.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/enable_rule_image.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/enable_rule_image.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/enable_rule_image.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/overview_video_description.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/overview_video_description.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/overview_video_description.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/overview_video_description.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/video.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/video.test.tsx similarity index 97% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/video.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/video.test.tsx index a02c89399ac99..ff891e1f1dd70 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/video.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/video.test.tsx @@ -28,6 +28,7 @@ jest.mock('@elastic/eui', () => ({ EuiFlexItem: ({ children }: { children: React.ReactElement }) =>
{children}
, EuiIcon: () => , useEuiTheme: () => ({ euiTheme: { colors: { fullShade: '#000', emptyShade: '#fff' } } }), + EuiCodeBlock: () => , })); describe('Video Component', () => { diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/video.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/video.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/video.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/video.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/view_alerts_image.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/view_alerts_image.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/view_alerts_image.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/view_alerts_image.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/view_dashboard_image.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/view_dashboard_image.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/view_dashboard_image.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/content/view_dashboard_image.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/helpers.test.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/helpers.test.ts similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/helpers.test.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/helpers.test.ts diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/helpers.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/helpers.ts similarity index 92% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/helpers.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/helpers.ts index 64d419b7d4c32..369cda7d65140 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/helpers.ts @@ -7,8 +7,8 @@ import type { MutableRefObject } from 'react'; import type { HttpSetup } from '@kbn/core/public'; -import { ENABLED_FIELD } from '@kbn/security-solution-plugin/common'; import { fetchRuleManagementFilters } from '../apis'; +import { ENABLED_FIELD } from '../../../../../../common/detection_engine/rule_management/rule_fields'; export const autoCheckPrebuildRuleStepCompleted = async ({ abortSignal, diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/index.test.tsx similarity index 95% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/index.test.tsx index 8678714d67af9..e4e4ad875261c 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/index.test.tsx @@ -28,11 +28,7 @@ jest.mock('./step_content', () => ({ jest.mock('../context/step_context'); jest.mock('../apis'); -jest.mock('../../common/services'); - -jest.mock('@kbn/security-solution-plugin/public', () => ({ - useSourcererDataView: jest.fn().mockReturnValue({ indicesExist: false }), -})); +jest.mock('../../../../lib/kibana'); jest.mock('@kbn/security-solution-navigation', () => ({ useNavigateTo: jest.fn().mockReturnValue({ navigateTo: jest.fn() }), diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/index.tsx similarity index 84% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/index.tsx index 5513bb967aa8b..1515a948341e7 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/index.tsx @@ -103,14 +103,21 @@ const CardStepComponent: React.FC<{ } = useCardStepStyles(); const stepGroundStyles = getStepGroundStyles({ hasStepContent }); - const panelClassNames = classnames({ - 'step-panel-collapsed': !isExpandedStep, - }); + const panelClassNames = classnames( + { + 'step-panel-collapsed': !isExpandedStep, + }, + stepPanelStyles + ); const stepIconClassNames = classnames('step-icon', { 'step-icon-done': isDone, + stepIconStyles, }); + const stepTitleClassNames = classnames('step-title', stepTitleStyles); + const allDoneTextNames = classnames('all-done-badge', allDoneTextStyles); + return ( - - - + + + {icon && } - - - {title} - + + {title} - +
{isDone && ( - + {ALL_DONE_TEXT} )} diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/step_content.test.tsx similarity index 97% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/step_content.test.tsx index cbaf99f808f4f..2d67c587dce51 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/step_content.test.tsx @@ -11,7 +11,7 @@ import { QuickStartSectionCardsId, SectionId } from '../types'; import { overviewVideoSteps } from '../sections'; jest.mock('../context/step_context'); -jest.mock('../../common/services'); +jest.mock('../../../../lib/kibana'); describe('StepContent', () => { const toggleTaskCompleteStatus = jest.fn(); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/step_content.tsx similarity index 69% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/step_content.tsx index 5fe1747fa0a2d..47481d7ece402 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/card_step/step_content.tsx @@ -6,7 +6,9 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import classnames from 'classnames'; import React from 'react'; + import { useCheckStepCompleted } from '../hooks/use_check_step_completed'; import { useStepContentStyles } from '../styles/step_content.styles'; import type { @@ -51,24 +53,34 @@ const StepContentComponent = ({ toggleTaskCompleteStatus, }); + const stepContentGroupClassName = classnames('step-content-group', stepContentGroupStyles); + const leftContentClassNames = classnames('left-panel', leftContentStyles); + + const descriptionClassNames = classnames( + 'step-content-description', + 'eui-displayBlock', + descriptionStyles + ); + + const rightPanelClassNames = classnames('right-panel', rightPanelStyles); + + const rightPanelContentClassNames = classnames('right-panel-wrapper', rightPanelContentStyles); return ( {step.description && ( - + {step.description.map((desc, index) => (
{desc}
@@ -77,17 +89,8 @@ const StepContentComponent = ({
)} {splitPanel && ( - - {splitPanel && ( -
- {splitPanel} -
- )} + + {splitPanel &&
{splitPanel}
}
)}
diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/configs.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/configs.ts new file mode 100644 index 0000000000000..dcf2a2e4c990b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/configs.ts @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/** + * We do not want serverless code in this plugin. Please Do Not Reuse. + * This is temporary, will be deleted when issue-174766 is resolved. + */ +export enum ProductLine { + security = 'security', + endpoint = 'endpoint', + cloud = 'cloud', +} +/** + * We do not want serverless code in this plugin. Please Do Not Reuse. + * This is temporary, will be deleted when issue-174766 is resolved. + */ +export enum ProductTier { + essentials = 'essentials', + complete = 'complete', +} + +/** + * We do not want serverless code in this plugin. Please Do Not Reuse. + * This is temporary, will be deleted when issue-174766 is resolved. + */ +export type SecurityProductTypes = Array<{ + product_line: ProductLine; + product_tier: ProductTier; +}>; + +/** + * We do not want serverless code in this plugin. Please Do Not Reuse. + * This is temporary, will be deleted when issue-174766 is resolved. + */ +export const ALL_PRODUCT_LINES = Object.values(ProductLine); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/context/__mocks__/step_context.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/context/__mocks__/step_context.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/context/__mocks__/step_context.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/context/__mocks__/step_context.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/context/step_context.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/context/step_context.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/context/step_context.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/context/step_context.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/footer/footer.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/footer/footer.ts similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/footer/footer.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/footer/footer.ts diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/footer/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/footer/index.tsx similarity index 86% rename from x-pack/plugins/security_solution_serverless/public/get_started/footer/index.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/footer/index.tsx index d2eabe9364c40..9d026f12b4925 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/footer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/footer/index.tsx @@ -7,6 +7,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer } from '@elastic/eui'; import React, { useMemo } from 'react'; + import { useFooterStyles } from '../styles/footer.styles'; import { getFooter } from './footer'; @@ -18,16 +19,16 @@ const FooterComponent = () => { alignItems="center" justifyContent="spaceBetween" gutterSize="none" - css={wrapperStyle} + className={wrapperStyle} > {footer.map((item) => ( {item.title} -

{item.title}

-

{item.description}

+

{item.title}

+

{item.description}

- + {item.link.title}
diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/footer/translations.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/footer/translations.ts similarity index 65% rename from x-pack/plugins/security_solution_serverless/public/get_started/footer/translations.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/footer/translations.ts index 9a924181c43f7..4bb41d883e785 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/footer/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/footer/translations.ts @@ -8,84 +8,84 @@ import { i18n } from '@kbn/i18n'; export const FOOTER_DOCUMENTATION_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.documentation.title', + 'xpack.securitySolution.onboarding.footer.documentation.title', { defaultMessage: 'Browse documentation', } ); export const FOOTER_DOCUMENTATION_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.documentation.description', + 'xpack.securitySolution.onboarding.footer.documentation.description', { defaultMessage: 'In-depth guides on all Elastic features', } ); export const FOOTER_DOCUMENTATION_LINK_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.documentation.link.title', + 'xpack.securitySolution.onboarding.footer.documentation.link.title', { defaultMessage: 'Start reading', } ); export const FOOTER_FORUM_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.forum.title', + 'xpack.securitySolution.onboarding.footer.forum.title', { defaultMessage: 'Explore forum', } ); export const FOOTER_FORUM_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.forum.description', + 'xpack.securitySolution.onboarding.footer.forum.description', { defaultMessage: 'Exchange thoughts about Elastic', } ); export const FOOTER_FORUM_LINK_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.forum.link.title', + 'xpack.securitySolution.onboarding.footer.forum.link.title', { defaultMessage: 'Discuss Forum', } ); export const FOOTER_DEMO_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.demo.title', + 'xpack.securitySolution.onboarding.footer.demo.title', { defaultMessage: 'View demo project', } ); export const FOOTER_DEMO_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.demo.description', + 'xpack.securitySolution.onboarding.footer.demo.description', { defaultMessage: 'Discover Elastic using sample data', } ); export const FOOTER_DEMO_LINK_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.demo.link.title', + 'xpack.securitySolution.onboarding.footer.demo.link.title', { defaultMessage: 'Explore demo', } ); export const FOOTER_LABS_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.labs.title', + 'xpack.securitySolution.onboarding.footer.labs.title', { defaultMessage: 'Elastic Security Labs', } ); export const FOOTER_LABS_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.labs.description', + 'xpack.securitySolution.onboarding.footer.labs.description', { defaultMessage: 'Insights from security researchers', } ); export const FOOTER_LABS_LINK_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.footer.labs.link.title', + 'xpack.securitySolution.onboarding.footer.labs.link.title', { defaultMessage: 'Learn more', } diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.test.ts similarity index 94% rename from x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.test.ts index e48eab5b0fe8b..9fa382567b79e 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.test.ts @@ -28,8 +28,18 @@ import { } from './types'; import * as sectionsConfigs from './sections'; -import { ProductLine } from '../../common/product'; +import { ProductLine } from './configs'; const mockSections = jest.spyOn(sectionsConfigs, 'getSections'); + +const onboardingSteps = [ + CreateProjectSteps.createFirstProject, + OverviewSteps.getToKnowElasticSecurity, + AddIntegrationsSteps.connectToDataSources, + ViewDashboardSteps.analyzeData, + EnablePrebuiltRulesSteps.enablePrebuiltRules, + ViewAlertsSteps.viewAlerts, +]; + describe('getCardTimeInMinutes', () => { it('should calculate the total time in minutes for a card correctly', () => { const card = { @@ -131,7 +141,7 @@ describe('setupActiveSections', () => { it('should set up active steps based on active products', () => { const finishedSteps = {} as unknown as Record>; const activeProducts = new Set([ProductLine.cloud]); - const { activeSections } = setupActiveSections(finishedSteps, activeProducts); + const { activeSections } = setupActiveSections(finishedSteps, activeProducts, onboardingSteps); expect( getCard(QuickStartSectionCardsId.createFirstProject, SectionId.quickStart, activeSections) @@ -184,7 +194,7 @@ describe('setupActiveSections', () => { } as unknown as Record>; const activeProducts = new Set([ProductLine.security]); - const { activeSections } = setupActiveSections(finishedSteps, activeProducts); + const { activeSections } = setupActiveSections(finishedSteps, activeProducts, onboardingSteps); expect( getCard(QuickStartSectionCardsId.createFirstProject, SectionId.quickStart, activeSections) @@ -201,7 +211,7 @@ describe('setupActiveSections', () => { const activeProducts: Set = new Set(); - const activeSections = setupActiveSections(finishedSteps, activeProducts); + const activeSections = setupActiveSections(finishedSteps, activeProducts, onboardingSteps); expect(activeSections).toEqual({ activeSections: null, @@ -222,9 +232,9 @@ describe('setupActiveSections', () => { CreateProjectSteps.createFirstProject, ]), } as unknown as Record>; - const activeProducts = new Set([ProductLine.security]); + const activeProducts: Set = new Set([ProductLine.security]); - const activeSections = setupActiveSections(finishedSteps, activeProducts); + const activeSections = setupActiveSections(finishedSteps, activeProducts, onboardingSteps); expect(activeSections).toEqual({ activeSections: {}, @@ -273,6 +283,7 @@ describe('updateActiveSections', () => { activeSections: testActiveSections, cardId, finishedSteps, + onboardingSteps, sectionId, }); @@ -302,6 +313,7 @@ describe('updateActiveSections', () => { const updatedSections = updateActiveSections({ activeProducts, finishedSteps, + onboardingSteps, activeSections: null, sectionId, cardId, @@ -322,6 +334,7 @@ describe('updateActiveSections', () => { const updatedSections = updateActiveSections({ activeProducts, finishedSteps, + onboardingSteps, activeSections, sectionId, cardId, diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.ts similarity index 81% rename from x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.ts index e1c1719f35493..6bf7ff9cced91 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ProductLine } from '../../common/product'; +import type { ProductLine } from './configs'; import { getSections } from './sections'; import type { ActiveCard, @@ -21,12 +21,15 @@ import { CreateProjectSteps, QuickStartSectionCardsId } from './types'; export const CONTENT_WIDTH = 1150; -export const defaultFinishedSteps: Partial> = { +export const DEFAULT_FINISHED_STEPS: Partial> = { [QuickStartSectionCardsId.createFirstProject]: [CreateProjectSteps.createFirstProject], }; -export const isDefaultFinishedCardStep = (cardId: CardId, stepId: StepId) => - !!defaultFinishedSteps[cardId]?.includes(stepId); +export const isDefaultFinishedCardStep = ( + cardId: CardId, + stepId: StepId, + onboardingSteps: StepId[] +) => !!DEFAULT_FINISHED_STEPS[cardId]?.includes(stepId) && onboardingSteps?.includes(stepId); export const getCardTimeInMinutes = (activeSteps: Step[] | undefined, stepsDone: Set) => activeSteps?.reduce( @@ -36,14 +39,18 @@ export const getCardTimeInMinutes = (activeSteps: Step[] | undefined, stepsDone: ) ?? 0; export const getCardStepsLeft = (activeSteps: Step[] | undefined, stepsDone: Set) => - (activeSteps?.length ?? 0) - (stepsDone.size ?? 0); + Math.max((activeSteps?.length ?? 0) - (stepsDone.size ?? 0), 0); export const isStepActive = (step: Step, activeProducts: Set) => !step.productLineRequired || step.productLineRequired?.some((condition) => activeProducts.has(condition)); -export const getActiveSteps = (steps: Step[] | undefined, activeProducts: Set) => - steps?.filter((step) => isStepActive(step, activeProducts)); +export const getActiveSteps = ( + steps: Step[] | undefined, + activeProducts: Set, + onboardingSteps: StepId[] +) => + steps?.filter((step) => onboardingSteps.includes(step.id) && isStepActive(step, activeProducts)); const getfinishedActiveSteps = ( finishedStepIds: StepId[] | undefined, @@ -101,27 +108,30 @@ export const getStepsByActiveProduct = ({ activeProducts, cardId, sectionId, + onboardingSteps, }: { activeProducts: Set; cardId: CardId; sectionId: SectionId; + onboardingSteps: StepId[]; }) => { const card = getCard({ cardId, sectionId }); - const steps = getActiveSteps(card?.steps, activeProducts); + const steps = getActiveSteps(card?.steps, activeProducts, onboardingSteps); return steps; }; export const setupActiveSections = ( finishedSteps: Record>, - activeProducts: Set + activeProducts: Set, + onboardingSteps: StepId[] ) => activeProducts.size > 0 ? getSections().reduce( (acc, section) => { const activeCards = section.cards?.reduce((accCards, card) => { - const activeSteps = getActiveSteps(card.steps, activeProducts); + const activeSteps = getActiveSteps(card.steps, activeProducts, onboardingSteps); const activeStepIds = activeSteps?.map(({ id }) => id); const stepsDone: Set = getfinishedActiveSteps( finishedSteps[card.id] ? [...finishedSteps[card.id]] : undefined, @@ -132,12 +142,14 @@ export const setupActiveSections = ( acc.totalStepsLeft += stepsLeft; acc.totalActiveSteps += activeStepIds?.length ?? 0; - accCards[card.id] = { - id: card.id, - timeInMins, - stepsLeft, - activeStepIds, - }; + if (activeSteps && activeSteps.length > 0) { + accCards[card.id] = { + id: card.id, + timeInMins, + stepsLeft, + activeStepIds, + }; + } return accCards; }, {} as Record) ?? {}; @@ -156,12 +168,14 @@ export const updateActiveSections = ({ activeSections, cardId, finishedSteps, + onboardingSteps, sectionId, }: { activeProducts: Set; activeSections: ActiveSections | null; cardId: CardId; finishedSteps: Record>; + onboardingSteps: StepId[]; sectionId: SectionId; }): { activeSections: ActiveSections | null; @@ -175,7 +189,7 @@ export const updateActiveSections = ({ return { activeSections, totalActiveSteps: null, totalStepsLeft: null }; } - const steps = getStepsByActiveProduct({ activeProducts, cardId, sectionId }); + const steps = getStepsByActiveProduct({ activeProducts, cardId, sectionId, onboardingSteps }); const activeStepIds = activeCard.activeStepIds; const stepsDone: Set = getfinishedActiveSteps( @@ -190,12 +204,16 @@ export const updateActiveSections = ({ ...activeSections, [sectionId]: { ...activeSections[sectionId], - [cardId]: { - id: cardId, - timeInMins, - stepsLeft, - activeStepIds, - }, + ...(activeStepIds && activeStepIds?.length > 0 + ? { + [cardId]: { + id: cardId, + timeInMins, + stepsLeft, + activeStepIds, + }, + } + : {}), }, }; diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_available_steps.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_available_steps.ts new file mode 100644 index 0000000000000..4e757625b8bb8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_available_steps.ts @@ -0,0 +1,24 @@ +/* + * 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 { + AddIntegrationsSteps, + CreateProjectSteps, + EnablePrebuiltRulesSteps, + OverviewSteps, + ViewAlertsSteps, + ViewDashboardSteps, +} from '../../types'; + +export const useAvailableSteps = jest.fn(() => [ + CreateProjectSteps.createFirstProject, + OverviewSteps.getToKnowElasticSecurity, + AddIntegrationsSteps.connectToDataSources, + ViewDashboardSteps.analyzeData, + EnablePrebuiltRulesSteps.enablePrebuiltRules, + ViewAlertsSteps.viewAlerts, +]); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_product_types.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_product_types.ts new file mode 100644 index 0000000000000..ed2bcfc3bacc0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_product_types.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ProductLine, ProductTier } from '../../configs'; + +export const useProductTypes = jest.fn(() => [ + { product_line: ProductLine.security, product_tier: ProductTier.complete }, +]); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_project_features_url.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_project_features_url.ts new file mode 100644 index 0000000000000..dc08286e35d25 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_project_features_url.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export const useProjectFeaturesUrl = jest.fn(() => 'mocked_user_name'); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_projects_url.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_projects_url.ts new file mode 100644 index 0000000000000..d7e75e6bda2aa --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/__mocks__/use_projects_url.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export const useProjectsUrl = jest.fn(() => 'mock_projects_url'); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_available_steps.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_available_steps.ts new file mode 100644 index 0000000000000..e4548ed3f4cb0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_available_steps.ts @@ -0,0 +1,13 @@ +/* + * 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 { useObservable } from 'react-use'; +import { useKibana } from '../../../../lib/kibana'; + +export const useAvailableSteps = () => { + const { availableSteps$ } = useKibana().services.onboarding; + return useObservable(availableSteps$); +}; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_check_step_completed.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_check_step_completed.test.tsx similarity index 90% rename from x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_check_step_completed.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_check_step_completed.test.tsx index efb3cfdd4385e..cf7336a4cdce7 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_check_step_completed.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_check_step_completed.test.tsx @@ -14,18 +14,7 @@ import { SectionId, } from '../types'; -jest.mock('../../common/services', () => ({ - useKibana: () => ({ - services: { - http: {}, - notifications: { - toasts: { - addError: jest.fn(), - }, - }, - }, - }), -})); +jest.mock('../../../../lib/kibana'); describe('useCheckStepCompleted', () => { it('does nothing when autoCheckIfStepCompleted is not provided', () => { diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_check_step_completed.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_check_step_completed.tsx similarity index 97% rename from x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_check_step_completed.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_check_step_completed.tsx index 8170b758812d7..05cb6825ab3a4 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_check_step_completed.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_check_step_completed.tsx @@ -6,7 +6,7 @@ */ import { useEffect, useRef } from 'react'; -import { useKibana } from '../../common/services'; +import { useKibana } from '../../../../lib/kibana'; import type { StepId, CardId, diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_product_types.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_product_types.ts new file mode 100644 index 0000000000000..dda575c3b789f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_product_types.ts @@ -0,0 +1,14 @@ +/* + * 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 { useObservable } from 'react-use'; +import { useKibana } from '../../../../lib/kibana'; + +export const useProductTypes = () => { + const { productTypes$ } = useKibana().services.onboarding; + return useObservable(productTypes$, undefined); +}; diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_project_features_url.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_project_features_url.ts new file mode 100644 index 0000000000000..c65eea5cca031 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_project_features_url.ts @@ -0,0 +1,14 @@ +/* + * 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 { useObservable } from 'react-use'; +import { useKibana } from '../../../../lib/kibana'; + +export const useProjectFeaturesUrl = () => { + const { projectFeaturesUrl$ } = useKibana().services.onboarding; + return useObservable(projectFeaturesUrl$, undefined); +}; diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_projects_url.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_projects_url.ts new file mode 100644 index 0000000000000..9fc8f4f06ea02 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_projects_url.ts @@ -0,0 +1,14 @@ +/* + * 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 { useObservable } from 'react-use'; +import { useKibana } from '../../../../lib/kibana'; + +export const useProjectsUrl = () => { + const { projectsUrl$ } = useKibana().services.onboarding; + return useObservable(projectsUrl$, undefined); +}; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_scroll.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_scroll.ts similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_scroll.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_scroll.ts diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_setup_sections.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_setup_sections.test.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_setup_sections.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_setup_sections.test.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_setup_sections.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_setup_sections.tsx similarity index 99% rename from x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_setup_sections.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_setup_sections.tsx index b1f5bedcc2779..295d6565a4cf1 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_setup_sections.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_setup_sections.tsx @@ -8,7 +8,7 @@ import type { EuiThemeComputed } from '@elastic/eui'; import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import React, { useCallback } from 'react'; -import { css } from '@emotion/react'; +import { css } from '@emotion/css'; import type { ActiveSections, CardId, diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_toggle_panel.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_toggle_panel.test.tsx similarity index 86% rename from x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_toggle_panel.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_toggle_panel.test.tsx index 7126f58106a98..61fac46b03576 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_toggle_panel.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_toggle_panel.test.tsx @@ -6,9 +6,9 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; import { useTogglePanel } from './use_toggle_panel'; -import { getStartedStorage } from '../storage'; -import { ProductLine } from '../../../common/product'; -import type { SecurityProductTypes } from '../../../common/config'; +import { onboardingStorage } from '../storage'; + +import type { StepId } from '../types'; import { QuickStartSectionCardsId, SectionId, @@ -21,9 +21,10 @@ import { ViewAlertsSteps, EnablePrebuiltRulesSteps, } from '../types'; +import type { SecurityProductTypes } from '../configs'; +import { ProductLine } from '../configs'; jest.mock('../storage'); -jest.mock('../../common/services'); jest.mock('react-router-dom', () => ({ useLocation: jest.fn().mockReturnValue({ hash: '' }), })); @@ -41,15 +42,24 @@ describe('useTogglePanel', () => { { product_line: 'endpoint', product_tier: 'complete' }, ] as SecurityProductTypes; + const onboardingSteps: StepId[] = [ + CreateProjectSteps.createFirstProject, + OverviewSteps.getToKnowElasticSecurity, + AddIntegrationsSteps.connectToDataSources, + ViewDashboardSteps.analyzeData, + EnablePrebuiltRulesSteps.enablePrebuiltRules, + ViewAlertsSteps.viewAlerts, + ]; + beforeEach(() => { jest.clearAllMocks(); - (getStartedStorage.getAllFinishedStepsFromStorage as jest.Mock).mockReturnValue({ + (onboardingStorage.getAllFinishedStepsFromStorage as jest.Mock).mockReturnValue({ [QuickStartSectionCardsId.createFirstProject]: new Set([ CreateProjectSteps.createFirstProject, ]), }); - (getStartedStorage.getActiveProductsFromStorage as jest.Mock).mockReturnValue([ + (onboardingStorage.getActiveProductsFromStorage as jest.Mock).mockReturnValue([ ProductLine.security, ProductLine.cloud, ProductLine.endpoint, @@ -57,9 +67,9 @@ describe('useTogglePanel', () => { }); test('should initialize state with correct initial values - when no active products from local storage', () => { - (getStartedStorage.getActiveProductsFromStorage as jest.Mock).mockReturnValue([]); + (onboardingStorage.getActiveProductsFromStorage as jest.Mock).mockReturnValue([]); - const { result } = renderHook(() => useTogglePanel({ productTypes })); + const { result } = renderHook(() => useTogglePanel({ productTypes, onboardingSteps })); const { state } = result.current; @@ -119,7 +129,7 @@ describe('useTogglePanel', () => { }); test('should initialize state with correct initial values - when all products active', () => { - const { result } = renderHook(() => useTogglePanel({ productTypes })); + const { result } = renderHook(() => useTogglePanel({ productTypes, onboardingSteps })); const { state } = result.current; @@ -181,10 +191,10 @@ describe('useTogglePanel', () => { }); test('should initialize state with correct initial values - when security product active', () => { - (getStartedStorage.getActiveProductsFromStorage as jest.Mock).mockReturnValue([ + (onboardingStorage.getActiveProductsFromStorage as jest.Mock).mockReturnValue([ ProductLine.security, ]); - const { result } = renderHook(() => useTogglePanel({ productTypes })); + const { result } = renderHook(() => useTogglePanel({ productTypes, onboardingSteps })); const { state } = result.current; @@ -244,7 +254,7 @@ describe('useTogglePanel', () => { }); test('should reset all the card steps in storage when a step is expanded. (As it allows only one step open at a time)', () => { - const { result } = renderHook(() => useTogglePanel({ productTypes })); + const { result } = renderHook(() => useTogglePanel({ productTypes, onboardingSteps })); const { onStepClicked } = result.current; @@ -257,11 +267,11 @@ describe('useTogglePanel', () => { }); }); - expect(getStartedStorage.resetAllExpandedCardStepsToStorage).toHaveBeenCalledTimes(1); + expect(onboardingStorage.resetAllExpandedCardStepsToStorage).toHaveBeenCalledTimes(1); }); test('should add the current step to storage when it is expanded', () => { - const { result } = renderHook(() => useTogglePanel({ productTypes })); + const { result } = renderHook(() => useTogglePanel({ productTypes, onboardingSteps })); const { onStepClicked } = result.current; @@ -274,15 +284,15 @@ describe('useTogglePanel', () => { }); }); - expect(getStartedStorage.addExpandedCardStepToStorage).toHaveBeenCalledTimes(1); - expect(getStartedStorage.addExpandedCardStepToStorage).toHaveBeenCalledWith( + expect(onboardingStorage.addExpandedCardStepToStorage).toHaveBeenCalledTimes(1); + expect(onboardingStorage.addExpandedCardStepToStorage).toHaveBeenCalledWith( QuickStartSectionCardsId.watchTheOverviewVideo, OverviewSteps.getToKnowElasticSecurity ); }); test('should remove the current step from storage when it is collapsed', () => { - const { result } = renderHook(() => useTogglePanel({ productTypes })); + const { result } = renderHook(() => useTogglePanel({ productTypes, onboardingSteps })); const { onStepClicked } = result.current; @@ -295,15 +305,15 @@ describe('useTogglePanel', () => { }); }); - expect(getStartedStorage.removeExpandedCardStepFromStorage).toHaveBeenCalledTimes(1); - expect(getStartedStorage.removeExpandedCardStepFromStorage).toHaveBeenCalledWith( + expect(onboardingStorage.removeExpandedCardStepFromStorage).toHaveBeenCalledTimes(1); + expect(onboardingStorage.removeExpandedCardStepFromStorage).toHaveBeenCalledWith( QuickStartSectionCardsId.watchTheOverviewVideo, OverviewSteps.getToKnowElasticSecurity ); }); test('should call addFinishedStepToStorage when toggleTaskCompleteStatus is executed', () => { - const { result } = renderHook(() => useTogglePanel({ productTypes })); + const { result } = renderHook(() => useTogglePanel({ productTypes, onboardingSteps })); const { toggleTaskCompleteStatus } = result.current; @@ -315,15 +325,15 @@ describe('useTogglePanel', () => { }); }); - expect(getStartedStorage.addFinishedStepToStorage).toHaveBeenCalledTimes(1); - expect(getStartedStorage.addFinishedStepToStorage).toHaveBeenCalledWith( + expect(onboardingStorage.addFinishedStepToStorage).toHaveBeenCalledTimes(1); + expect(onboardingStorage.addFinishedStepToStorage).toHaveBeenCalledWith( QuickStartSectionCardsId.watchTheOverviewVideo, OverviewSteps.getToKnowElasticSecurity ); }); test('should call removeFinishedStepToStorage when toggleTaskCompleteStatus is executed with undo equals to true', () => { - const { result } = renderHook(() => useTogglePanel({ productTypes })); + const { result } = renderHook(() => useTogglePanel({ productTypes, onboardingSteps })); const { toggleTaskCompleteStatus } = result.current; @@ -336,15 +346,16 @@ describe('useTogglePanel', () => { }); }); - expect(getStartedStorage.removeFinishedStepFromStorage).toHaveBeenCalledTimes(1); - expect(getStartedStorage.removeFinishedStepFromStorage).toHaveBeenCalledWith( + expect(onboardingStorage.removeFinishedStepFromStorage).toHaveBeenCalledTimes(1); + expect(onboardingStorage.removeFinishedStepFromStorage).toHaveBeenCalledWith( QuickStartSectionCardsId.watchTheOverviewVideo, - OverviewSteps.getToKnowElasticSecurity + OverviewSteps.getToKnowElasticSecurity, + onboardingSteps ); }); test('should call toggleActiveProductsInStorage when onProductSwitchChanged is executed', () => { - const { result } = renderHook(() => useTogglePanel({ productTypes })); + const { result } = renderHook(() => useTogglePanel({ productTypes, onboardingSteps })); const { onProductSwitchChanged } = result.current; @@ -352,8 +363,8 @@ describe('useTogglePanel', () => { onProductSwitchChanged({ id: ProductLine.security, label: 'Analytics' }); }); - expect(getStartedStorage.toggleActiveProductsInStorage).toHaveBeenCalledTimes(1); - expect(getStartedStorage.toggleActiveProductsInStorage).toHaveBeenCalledWith( + expect(onboardingStorage.toggleActiveProductsInStorage).toHaveBeenCalledTimes(1); + expect(onboardingStorage.toggleActiveProductsInStorage).toHaveBeenCalledWith( ProductLine.security ); }); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_toggle_panel.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_toggle_panel.tsx similarity index 82% rename from x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_toggle_panel.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_toggle_panel.tsx index e2a0dbf72cc69..81d4b6557239b 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_toggle_panel.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/hooks/use_toggle_panel.tsx @@ -7,11 +7,11 @@ import { useCallback, useEffect, useMemo, useReducer } from 'react'; import { useLocation } from 'react-router-dom'; -import { SecurityPageName } from '@kbn/security-solution-plugin/common'; import { useNavigateTo } from '@kbn/security-solution-navigation'; -import { ProductLine } from '../../../common/product'; -import type { SecurityProductTypes } from '../../../common/config'; -import { getStartedStorage } from '../storage'; + +import { SecurityPageName } from '../../../../../../common'; + +import { onboardingStorage } from '../storage'; import { getActiveSectionsInitialStates, getActiveProductsInitialStates, @@ -25,17 +25,20 @@ import type { OnStepClicked, Step, Switch, + StepId, } from '../types'; -import { GetStartedPageActions } from '../types'; +import { OnboardingActions } from '../types'; import { findCardSectionByStepId } from '../helpers'; +import type { SecurityProductTypes } from '../configs'; +import { ALL_PRODUCT_LINES, ProductLine } from '../configs'; const syncExpandedCardStepsToStorageFromURL = (maybeStepId: string) => { const { matchedCard, matchedStep } = findCardSectionByStepId(maybeStepId); const hasStepContent = matchedStep && matchedStep.description; if (matchedCard && matchedStep && hasStepContent) { - getStartedStorage.resetAllExpandedCardStepsToStorage(); - getStartedStorage.addExpandedCardStepToStorage(matchedCard.id, matchedStep.id); + onboardingStorage.resetAllExpandedCardStepsToStorage(); + onboardingStorage.addExpandedCardStepToStorage(matchedCard.id, matchedStep.id); } }; @@ -62,7 +65,13 @@ const syncExpandedCardStepsFromStorageToURL = ( } }; -export const useTogglePanel = ({ productTypes }: { productTypes: SecurityProductTypes }) => { +export const useTogglePanel = ({ + productTypes, + onboardingSteps, +}: { + productTypes?: SecurityProductTypes; + onboardingSteps: StepId[]; +}) => { const { navigateTo } = useNavigateTo(); const { hash: detailName } = useLocation(); @@ -78,7 +87,7 @@ export const useTogglePanel = ({ productTypes }: { productTypes: SecurityProduct removeExpandedCardStepFromStorage, resetAllExpandedCardStepsToStorage, getAllExpandedCardStepsFromStorage, - } = getStartedStorage; + } = onboardingStorage; const finishedStepsInitialStates = useMemo( () => getFinishedStepsInitialStates({ finishedSteps: getAllFinishedStepsFromStorage() }), @@ -91,8 +100,9 @@ export const useTogglePanel = ({ productTypes }: { productTypes: SecurityProduct }); return activeProductsFromStorage.size > 0 ? activeProductsFromStorage - : new Set(productTypes.map(({ product_line: productLine }) => ProductLine[productLine])) ?? - new Set([ProductLine.security, ProductLine.endpoint, ProductLine.cloud]); + : productTypes && productTypes.length > 0 + ? new Set(productTypes.map(({ product_line: productLine }) => ProductLine[productLine])) + : new Set(ALL_PRODUCT_LINES); }, [getActiveProductsFromStorage, productTypes]); const { @@ -104,8 +114,9 @@ export const useTogglePanel = ({ productTypes }: { productTypes: SecurityProduct getActiveSectionsInitialStates({ activeProducts: activeProductsInitialStates, finishedSteps: finishedStepsInitialStates, + onboardingSteps, }), - [activeProductsInitialStates, finishedStepsInitialStates] + [activeProductsInitialStates, finishedStepsInitialStates, onboardingSteps] ); const expandedCardsInitialStates: ExpandedCardSteps = useMemo(() => { @@ -119,7 +130,7 @@ export const useTogglePanel = ({ productTypes }: { productTypes: SecurityProduct const onStepClicked: OnStepClicked = useCallback( ({ stepId, cardId, isExpanded }) => { dispatch({ - type: GetStartedPageActions.ToggleExpandedStep, + type: OnboardingActions.ToggleExpandedStep, payload: { stepId, cardId, isStepExpanded: isExpanded }, }); if (isExpanded) { @@ -144,28 +155,27 @@ export const useTogglePanel = ({ productTypes }: { productTypes: SecurityProduct finishedSteps: finishedStepsInitialStates, totalActiveSteps: totalActiveStepsInitialStates, totalStepsLeft: totalStepsLeftInitialStates, + onboardingSteps, }); const toggleTaskCompleteStatus: ToggleTaskCompleteStatus = useCallback( ({ stepId, cardId, sectionId, undo }) => { dispatch({ - type: undo - ? GetStartedPageActions.RemoveFinishedStep - : GetStartedPageActions.AddFinishedStep, + type: undo ? OnboardingActions.RemoveFinishedStep : OnboardingActions.AddFinishedStep, payload: { stepId, cardId, sectionId }, }); if (undo) { - removeFinishedStepFromStorage(cardId, stepId); + removeFinishedStepFromStorage(cardId, stepId, state.onboardingSteps); } else { addFinishedStepToStorage(cardId, stepId); } }, - [addFinishedStepToStorage, removeFinishedStepFromStorage] + [addFinishedStepToStorage, removeFinishedStepFromStorage, state.onboardingSteps] ); const onProductSwitchChanged = useCallback( (section: Switch) => { - dispatch({ type: GetStartedPageActions.ToggleProduct, payload: { section: section.id } }); + dispatch({ type: OnboardingActions.ToggleProduct, payload: { section: section.id } }); toggleActiveProductsInStorage(section.id); }, [toggleActiveProductsInStorage] @@ -230,5 +240,10 @@ export const useTogglePanel = ({ productTypes }: { productTypes: SecurityProduct } }, [navigateTo, onStepClicked, state.expandedCardSteps, stepIdFromHash]); - return { state, onStepClicked, toggleTaskCompleteStatus, onProductSwitchChanged }; + return { + state, + onStepClicked, + toggleTaskCompleteStatus, + onProductSwitchChanged, + }; }; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/analyze_data_using_dashboards.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/analyze_data_using_dashboards.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/analyze_data_using_dashboards.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/analyze_data_using_dashboards.png diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/connect_to_existing_sources.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/connect_to_existing_sources.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/connect_to_existing_sources.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/connect_to_existing_sources.png diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/create_projects.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/create_projects.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/create_projects.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/create_projects.png diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/demo.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/demo.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/demo.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/demo.png diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/documentation.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/documentation.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/documentation.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/documentation.png diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/enable_prebuilt_rules.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/enable_prebuilt_rules.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/enable_prebuilt_rules.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/enable_prebuilt_rules.png diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/forum.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/forum.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/forum.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/forum.png diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/labs.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/labs.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/labs.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/labs.png diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/launch.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/launch.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/launch.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/launch.png diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/overview_video.svg b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/overview_video.svg similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/overview_video.svg rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/overview_video.svg diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/images/view_alerts.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/view_alerts.png similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/images/view_alerts.png rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/view_alerts.png diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/index.tsx new file mode 100644 index 0000000000000..819f8f9167660 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/index.tsx @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { Onboarding } from './lazy'; + +export const getOnboardingComponent = (): React.ComponentType<{ indicesExist?: boolean }> => + function OnBoardingComponent({ indicesExist }: { indicesExist?: boolean }) { + return ; + }; diff --git a/x-pack/plugins/security_solution_ess/public/get_started/lazy.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/lazy.tsx similarity index 72% rename from x-pack/plugins/security_solution_ess/public/get_started/lazy.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/lazy.tsx index 484a6b5913dd5..45001a2d9ce0d 100644 --- a/x-pack/plugins/security_solution_ess/public/get_started/lazy.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/lazy.tsx @@ -7,12 +7,12 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingLogo } from '@elastic/eui'; -const LandingCardsLazy = lazy(() => import('./landing_cards')); +const OnboardingLazy = lazy(() => import('./onboarding_with_settings')); const centerLogoStyle = { display: 'flex', margin: 'auto' }; -export const GetStarted = () => ( +export const Onboarding = ({ indicesExist }: { indicesExist?: boolean }) => ( }> - + ); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/get_started.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.test.tsx similarity index 65% rename from x-pack/plugins/security_solution_serverless/public/get_started/get_started.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.test.tsx index d1ff1e675f06f..5b86cbeaf1947 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/get_started.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.test.tsx @@ -6,11 +6,18 @@ */ import React from 'react'; import { render } from '@testing-library/react'; -import { GetStartedComponent } from './get_started'; -import type { SecurityProductTypes } from '../../common/config'; - +import { OnboardingComponent } from './onboarding'; +import { + AddIntegrationsSteps, + EnablePrebuiltRulesSteps, + OverviewSteps, + ViewAlertsSteps, + ViewDashboardSteps, +} from './types'; +import { ProductLine, ProductTier } from './configs'; jest.mock('./toggle_panel'); -jest.mock('../common/services'); +jest.mock('./hooks/use_project_features_url'); +jest.mock('./hooks/use_projects_url'); jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); return { @@ -30,7 +37,6 @@ jest.mock('@elastic/eui', () => { jest.mock('react-router-dom', () => ({ useLocation: jest.fn().mockReturnValue({ hash: '#watch_the_overview_video' }), })); -jest.mock('../common/hooks/use_user_name'); jest.mock('@kbn/security-solution-navigation', () => ({ useNavigateTo: jest.fn().mockReturnValue({ navigateTo: jest.fn() }), SecurityPageName: { @@ -38,17 +44,22 @@ jest.mock('@kbn/security-solution-navigation', () => ({ }, })); -const productTypes = [ - { product_line: 'security', product_tier: 'essentials' }, - { product_line: 'endpoint', product_tier: 'complete' }, - { product_line: 'cloud', product_tier: 'complete' }, -] as SecurityProductTypes; - -describe('GetStartedComponent', () => { +describe('OnboardingComponent', () => { + const props = { + indicesExist: true, + productTypes: [{ product_line: ProductLine.security, product_tier: ProductTier.complete }], + onboardingSteps: [ + OverviewSteps.getToKnowElasticSecurity, + AddIntegrationsSteps.connectToDataSources, + ViewDashboardSteps.analyzeData, + EnablePrebuiltRulesSteps.enablePrebuiltRules, + ViewAlertsSteps.viewAlerts, + ], + }; it('should render page title, subtitle, and description', () => { - const { getByText } = render(); + const { getByText } = render(); - const pageTitle = getByText('Hi mocked_user_name!'); + const pageTitle = getByText('Hi Unknown!'); const subtitle = getByText(`Get started with Security`); const description = getByText( `This area shows you everything you need to know. Feel free to explore all content. You can always come back later at any time.` @@ -60,7 +71,7 @@ describe('GetStartedComponent', () => { }); it('should render welcomeHeader and TogglePanel', () => { - const { getByTestId } = render(); + const { getByTestId } = render(); const welcomeHeader = getByTestId('welcome-header'); const togglePanel = getByTestId('toggle-panel'); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.tsx similarity index 59% rename from x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.tsx index 06ab6dd4991bc..096d3bb87c172 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/get_started.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.tsx @@ -5,29 +5,35 @@ * 2.0. */ -import { useEuiTheme } from '@elastic/eui'; import React from 'react'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; -import { css } from '@emotion/react'; + import { TogglePanel } from './toggle_panel'; -import type { SecurityProductTypes } from '../../common/config'; import { useTogglePanel } from './hooks/use_toggle_panel'; -import { ProductLine } from '../../common/product'; import { Progress } from './progress_bar'; import { StepContextProvider } from './context/step_context'; import { CONTENT_WIDTH } from './helpers'; import { WelcomeHeader } from './welcome_header'; import { Footer } from './footer'; import { useScrollToHash } from './hooks/use_scroll'; +import type { SecurityProductTypes } from './configs'; +import { ProductLine } from './configs'; + +import type { StepId } from './types'; +import { useOnboardingStyles } from './styles/onboarding.styles'; -export interface GetStartedProps { +interface OnboardingProps { indicesExist?: boolean; - productTypes: SecurityProductTypes; + productTypes: SecurityProductTypes | undefined; + onboardingSteps: StepId[]; } -export const GetStartedComponent: React.FC = ({ productTypes, indicesExist }) => { - const { euiTheme } = useEuiTheme(); +export const OnboardingComponent: React.FC = ({ + indicesExist, + productTypes, + onboardingSteps, +}) => { const { onStepClicked, toggleTaskCompleteStatus, @@ -39,45 +45,23 @@ export const GetStartedComponent: React.FC = ({ productTypes, i totalStepsLeft, expandedCardSteps, }, - } = useTogglePanel({ productTypes }); - const productTier = productTypes.find( + } = useTogglePanel({ productTypes, onboardingSteps }); + const productTier = productTypes?.find( (product) => product.product_line === ProductLine.security )?.product_tier; + const { wrapperStyles, progressSectionStyles, stepsSectionStyles } = useOnboardingStyles(); useScrollToHash(); return ( - - +
+ = ({ productTypes, i grow={true} restrictWidth={CONTENT_WIDTH} paddingSize="none" - css={css` - padding: 0 ${euiTheme.size.xxl} ${euiTheme.size.xxxl}; - background-color: ${euiTheme.colors.lightestShade}; - `} + className={stepsSectionStyles} > = ({ productTypes, i
- +
); }; -export const GetStarted = React.memo(GetStartedComponent); - -// eslint-disable-next-line import/no-default-export -export default GetStarted; +export const Onboarding = React.memo(OnboardingComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding_with_settings.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding_with_settings.tsx new file mode 100644 index 0000000000000..c84339b2ebfb6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding_with_settings.tsx @@ -0,0 +1,33 @@ +/* + * 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 { useAvailableSteps } from './hooks/use_available_steps'; +import { useProductTypes } from './hooks/use_product_types'; +import { Onboarding } from './onboarding'; + +export const OnboardingWithSettingsComponent: React.FC<{ indicesExist?: boolean }> = ({ + indicesExist, +}) => { + const productTypes = useProductTypes(); + const onboardingSteps = useAvailableSteps(); + + if (!onboardingSteps) { + return null; + } + + return ( + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default OnboardingWithSettingsComponent; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/product_switch.test.tsx similarity index 97% rename from x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/product_switch.test.tsx index 7f855c206274e..df9d8153a7702 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/product_switch.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { ProductSwitch } from './product_switch'; import type { EuiThemeComputed } from '@elastic/eui'; -import { ProductLine } from '../../common/product'; +import { ProductLine } from './configs'; describe('ProductSwitch', () => { const onProductSwitchChangedMock = jest.fn(); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/product_switch.tsx similarity index 95% rename from x-pack/plugins/security_solution_serverless/public/get_started/product_switch.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/product_switch.tsx index 62f58caaacd6d..8b373475ec9f7 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/product_switch.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/product_switch.tsx @@ -6,9 +6,9 @@ */ import { EuiPanel, EuiSwitch, EuiText, EuiTitle, type EuiThemeComputed } from '@elastic/eui'; -import { css } from '@emotion/react'; +import { css } from '@emotion/css'; import React, { useMemo } from 'react'; -import { ProductLine } from '../../common/product'; +import { ProductLine } from './configs'; import * as i18n from './translations'; import type { Switch } from './types'; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/progress_bar.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/progress_bar.tsx similarity index 86% rename from x-pack/plugins/security_solution_serverless/public/get_started/progress_bar.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/progress_bar.tsx index e886aca4ff50d..5d66bde56e2c1 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/progress_bar.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/progress_bar.tsx @@ -7,10 +7,10 @@ import { EuiFlexGroup, EuiFlexItem, EuiProgress, EuiSpacer } from '@elastic/eui'; import React from 'react'; -import type { ProductTier } from '../../common/product'; import { PROGRESS_TRACKER_LABEL } from './translations'; import { useProgressBarStyles } from './styles/progress_bar.style'; +import type { ProductTier } from './configs'; const ProgressComponent: React.FC<{ productTier: ProductTier | undefined; @@ -31,11 +31,11 @@ const ProgressComponent: React.FC<{ size="m" label={ - {PROGRESS_TRACKER_LABEL} + {PROGRESS_TRACKER_LABEL} } - valueText={{`${stepsDone}/${totalActiveSteps}`}} + valueText={{`${stepsDone}/${totalActiveSteps}`}} /> )} diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/reducer.test.ts similarity index 92% rename from x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/reducer.test.ts index 4d71a5c9ebf99..18c1bc240141b 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/reducer.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ProductLine } from '../../common/product'; +import { ProductLine } from './configs'; import { setupActiveSections } from './helpers'; import { reducer, @@ -25,7 +25,7 @@ import { AddIntegrationsSteps, CreateProjectSteps, EnablePrebuiltRulesSteps, - GetStartedPageActions, + OnboardingActions, GetStartedWithAlertsCardsId, OverviewSteps, QuickStartSectionCardsId, @@ -33,6 +33,14 @@ import { ViewAlertsSteps, ViewDashboardSteps, } from './types'; +const onboardingSteps = [ + CreateProjectSteps.createFirstProject, + OverviewSteps.getToKnowElasticSecurity, + AddIntegrationsSteps.connectToDataSources, + ViewDashboardSteps.analyzeData, + EnablePrebuiltRulesSteps.enablePrebuiltRules, + ViewAlertsSteps.viewAlerts, +]; describe('reducer', () => { it('should toggle section correctly', () => { @@ -44,7 +52,8 @@ describe('reducer', () => { } as Record>; const { activeSections, totalStepsLeft, totalActiveSteps } = setupActiveSections( finishedSteps, - activeProducts + activeProducts, + [OverviewSteps.getToKnowElasticSecurity] ); const initialState = { activeProducts: new Set([ProductLine.security]), @@ -53,10 +62,11 @@ describe('reducer', () => { totalStepsLeft, totalActiveSteps, expandedCardSteps: {} as ExpandedCardSteps, + onboardingSteps, }; const action: ToggleProductAction = { - type: GetStartedPageActions.ToggleProduct, + type: OnboardingActions.ToggleProduct, payload: { section: ProductLine.security }, }; @@ -75,7 +85,8 @@ describe('reducer', () => { } as Record>; const { activeSections, totalStepsLeft, totalActiveSteps } = setupActiveSections( finishedSteps, - activeProducts + activeProducts, + onboardingSteps ); const initialState = { activeProducts: new Set([ProductLine.security]), @@ -84,10 +95,11 @@ describe('reducer', () => { totalStepsLeft, totalActiveSteps, expandedCardSteps: {} as ExpandedCardSteps, + onboardingSteps, }; const action: AddFinishedStepAction = { - type: GetStartedPageActions.AddFinishedStep, + type: OnboardingActions.AddFinishedStep, payload: { cardId: QuickStartSectionCardsId.watchTheOverviewVideo, stepId: OverviewSteps.getToKnowElasticSecurity, @@ -189,6 +201,7 @@ describe('getActiveSectionsInitialStates', () => { } = getActiveSectionsInitialStates({ activeProducts, finishedSteps, + onboardingSteps, }); expect(initialStates).toEqual({ diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/reducer.tsx similarity index 87% rename from x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/reducer.tsx index 3d8cf2c7fa26e..11423a8d92ba8 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/reducer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/reducer.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import type { ProductLine } from '../../common/product'; +import type { ProductLine } from './configs'; import { setupActiveSections, updateActiveSections } from './helpers'; import type { ExpandedCardSteps, ReducerActions } from './types'; -import { type CardId, type StepId, type TogglePanelReducer, GetStartedPageActions } from './types'; +import { type CardId, type StepId, type TogglePanelReducer, OnboardingActions } from './types'; export const reducer = (state: TogglePanelReducer, action: ReducerActions): TogglePanelReducer => { - if (action.type === GetStartedPageActions.ToggleProduct) { + if (action.type === OnboardingActions.ToggleProduct) { const activeProducts = new Set([...state.activeProducts]); if (activeProducts.has(action.payload.section)) { @@ -22,7 +22,8 @@ export const reducer = (state: TogglePanelReducer, action: ReducerActions): Togg const { activeSections, totalStepsLeft, totalActiveSteps } = setupActiveSections( state.finishedSteps, - activeProducts + activeProducts, + state.onboardingSteps ); return { @@ -34,7 +35,7 @@ export const reducer = (state: TogglePanelReducer, action: ReducerActions): Togg }; } - if (action.type === GetStartedPageActions.AddFinishedStep) { + if (action.type === OnboardingActions.AddFinishedStep) { const finishedSteps = { ...state.finishedSteps, [action.payload.cardId]: state.finishedSteps[action.payload.cardId] @@ -50,6 +51,7 @@ export const reducer = (state: TogglePanelReducer, action: ReducerActions): Togg cardId: action.payload.cardId, finishedSteps, sectionId: action.payload.sectionId, + onboardingSteps: state.onboardingSteps, }); return { @@ -61,7 +63,7 @@ export const reducer = (state: TogglePanelReducer, action: ReducerActions): Togg }; } - if (action.type === GetStartedPageActions.RemoveFinishedStep) { + if (action.type === OnboardingActions.RemoveFinishedStep) { const finishedSteps = { ...state.finishedSteps, [action.payload.cardId]: state.finishedSteps[action.payload.cardId] @@ -77,6 +79,7 @@ export const reducer = (state: TogglePanelReducer, action: ReducerActions): Togg cardId: action.payload.cardId, finishedSteps, sectionId: action.payload.sectionId, + onboardingSteps: state.onboardingSteps, }); return { @@ -89,7 +92,7 @@ export const reducer = (state: TogglePanelReducer, action: ReducerActions): Togg } if ( - action.type === GetStartedPageActions.ToggleExpandedStep && + action.type === OnboardingActions.ToggleExpandedStep && action.payload.isStepExpanded != null ) { // It allows Only One step open at a time @@ -159,7 +162,9 @@ export const getActiveProductsInitialStates = ({ export const getActiveSectionsInitialStates = ({ activeProducts, finishedSteps, + onboardingSteps, }: { activeProducts: Set; finishedSteps: Record>; -}) => setupActiveSections(finishedSteps, activeProducts); + onboardingSteps: StepId[]; +}) => setupActiveSections(finishedSteps, activeProducts, onboardingSteps); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/sections.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/sections.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/sections.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/sections.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/add_elastic_rules_button.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/add_elastic_rules_button.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/step_links/add_elastic_rules_button.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/add_elastic_rules_button.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/add_integration_button.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/add_integration_button.tsx similarity index 80% rename from x-pack/plugins/security_solution_serverless/public/get_started/step_links/add_integration_button.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/add_integration_button.tsx index 59cb197cc276f..443f11920fd4d 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/add_integration_button.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/add_integration_button.tsx @@ -8,12 +8,15 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import { LinkButton } from '@kbn/security-solution-navigation/links'; -import { ExternalPageName } from '../../navigation/links/constants'; + +enum ExternalPageName { + integrationsSecurity = 'integrations:/browse/security', +} const AddIntegrationButtonComponent: React.FC = () => ( diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/add_integration_callout.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/add_integration_callout.tsx similarity index 75% rename from x-pack/plugins/security_solution_serverless/public/get_started/step_links/add_integration_callout.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/add_integration_callout.tsx index 5d5a707986d81..90de785d73ab5 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/add_integration_callout.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/add_integration_callout.tsx @@ -8,9 +8,9 @@ import React, { useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiCallOut, EuiIcon, EuiLink, useEuiTheme } from '@elastic/eui'; -import { SecurityPageName } from '@kbn/security-solution-plugin/common'; -import { useNavigateTo } from '@kbn/security-solution-navigation'; +import { SecurityPageName, useNavigateTo } from '@kbn/security-solution-navigation'; +import classnames from 'classnames'; import { useAddIntegrationsCalloutStyles } from '../styles/add_integrations_callout.styles'; import { ADD_INTEGRATIONS_STEP } from './translations'; import { AddIntegrationsSteps } from '../types'; @@ -28,9 +28,11 @@ const AddIntegrationsCalloutComponent = ({ stepName }: { stepName?: string }) => }); }, [navigateTo]); + const classNames = classnames('add-integrations-callout', calloutWrapperStyles); + return ( color={euiTheme.colors.title} className="eui-alignMiddle" /> - + + {ADD_INTEGRATIONS_STEP} - + ), stepName: stepName ?? ( ), @@ -62,7 +64,6 @@ const AddIntegrationsCalloutComponent = ({ stepName }: { stepName?: string }) => } size="s" - css={calloutWrapperStyles} /> ); }; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/alerts_link.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/alerts_link.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/step_links/alerts_link.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/alerts_link.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/dashboard_button.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/dashboard_button.tsx similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/step_links/dashboard_button.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/dashboard_button.tsx diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/manage_projects_button.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/manage_projects_button.tsx similarity index 75% rename from x-pack/plugins/security_solution_serverless/public/get_started/step_links/manage_projects_button.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/manage_projects_button.tsx index da282df1f5764..2bcc76f5e6aab 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/manage_projects_button.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/manage_projects_button.tsx @@ -7,26 +7,23 @@ import React from 'react'; import { EuiIcon, EuiButton } from '@elastic/eui'; -import { getCloudUrl } from '../../navigation/links/util'; import { MANAGE_PROJECTS } from './translations'; -import { useKibana } from '../../common/services'; +import { useProjectsUrl } from '../hooks/use_projects_url'; const ManageProjectsButtonComponent = () => { - const { cloud } = useKibana().services; - - const href = getCloudUrl('projects', cloud); - return ( + const projectsUrl = useProjectsUrl(); + return projectsUrl ? ( {MANAGE_PROJECTS} - ); + ) : null; }; export const ManageProjectsButton = React.memo(ManageProjectsButtonComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/translations.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/translations.ts similarity index 59% rename from x-pack/plugins/security_solution_serverless/public/get_started/step_links/translations.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/translations.ts index 5be1d44b11e01..e02e79b90b975 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/translations.ts @@ -8,56 +8,53 @@ import { i18n } from '@kbn/i18n'; export const MANAGE_PROJECTS = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.task.manageProjects', + 'xpack.securitySolution.onboarding.task.manageProjects', { defaultMessage: 'Manage projects', } ); export const ADD_ELASTIC_RULES = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.task.addElasticRules', + 'xpack.securitySolution.onboarding.task.addElasticRules', { defaultMessage: 'Add Elastic rules', } ); export const ADD_ELASTIC_RULES_CALLOUT_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.task.addElasticRules.callout.title', + 'xpack.securitySolution.onboarding.task.addElasticRules.callout.title', { defaultMessage: 'add Elastic rules', } ); export const ADD_INTEGRATIONS_STEP = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.task.addIntegrationsStep.title', + 'xpack.securitySolution.onboarding.task.addIntegrationsStep.title', { defaultMessage: 'Add integrations step', } ); -export const VIEW_ALERTS = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.task.viewAlerts', - { - defaultMessage: 'View alerts', - } -); +export const VIEW_ALERTS = i18n.translate('xpack.securitySolution.onboarding.task.viewAlerts', { + defaultMessage: 'View alerts', +}); export const VIEW_ALERTS_CALLOUT_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.task.viewAlerts.callout.title', + 'xpack.securitySolution.onboarding.task.viewAlerts.callout.title', { defaultMessage: 'view alerts', } ); export const GO_TO_DASHBOARDS = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.task.goToDashboards', + 'xpack.securitySolution.onboarding.task.goToDashboards', { defaultMessage: 'Go to dashboards', } ); export const VIEW_DASHBOARDS_CALLOUT_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.task.viewDashboards.callout.title', + 'xpack.securitySolution.onboarding.task.viewDashboards.callout.title', { defaultMessage: 'view dashboards', } diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/step_links/types.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/types.ts similarity index 100% rename from x-pack/plugins/security_solution_serverless/public/get_started/step_links/types.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/step_links/types.ts diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/storage.test.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/storage.test.ts similarity index 79% rename from x-pack/plugins/security_solution_serverless/public/get_started/storage.test.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/storage.test.ts index e2eb3c7398740..7130f7c614321 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/storage.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/storage.test.ts @@ -10,18 +10,36 @@ import { defaultExpandedCards, EXPANDED_CARDS_STORAGE_KEY, FINISHED_STEPS_STORAGE_KEY, - getStartedStorage, + onboardingStorage, } from './storage'; -import { CreateProjectSteps, OverviewSteps, QuickStartSectionCardsId, type StepId } from './types'; -import { storage } from '../common/lib/storage'; -import type { MockStorage } from '../common/lib/__mocks__/storage'; -import { ProductLine } from '../../common/product'; -import { defaultFinishedSteps } from './helpers'; +import { + AddIntegrationsSteps, + CreateProjectSteps, + EnablePrebuiltRulesSteps, + OverviewSteps, + QuickStartSectionCardsId, + ViewAlertsSteps, + ViewDashboardSteps, + type StepId, +} from './types'; +import { DEFAULT_FINISHED_STEPS } from './helpers'; +import type { MockStorage } from '../../../lib/local_storage/__mocks__'; +import { storage } from '../../../lib/local_storage'; +import { ProductLine } from './configs'; -jest.mock('../common/lib/storage'); +jest.mock('../../../lib/local_storage'); describe('useStorage', () => { const mockStorage = storage as unknown as MockStorage; + + const onboardingSteps = [ + CreateProjectSteps.createFirstProject, + OverviewSteps.getToKnowElasticSecurity, + AddIntegrationsSteps.connectToDataSources, + ViewDashboardSteps.analyzeData, + EnablePrebuiltRulesSteps.enablePrebuiltRules, + ViewAlertsSteps.viewAlerts, + ]; beforeEach(() => { // Clear the mocked storage object before each test mockStorage.clearMockStorageData(); @@ -29,17 +47,17 @@ describe('useStorage', () => { }); it('should return the active products from storage', () => { - expect(getStartedStorage.getActiveProductsFromStorage()).toEqual([]); + expect(onboardingStorage.getActiveProductsFromStorage()).toEqual([]); mockStorage.set(ACTIVE_PRODUCTS_STORAGE_KEY, [ProductLine.security, ProductLine.endpoint]); - expect(getStartedStorage.getActiveProductsFromStorage()).toEqual([ + expect(onboardingStorage.getActiveProductsFromStorage()).toEqual([ ProductLine.security, ProductLine.endpoint, ]); }); it('should toggle active products in storage', () => { - expect(getStartedStorage.toggleActiveProductsInStorage(ProductLine.security)).toEqual([ + expect(onboardingStorage.toggleActiveProductsInStorage(ProductLine.security)).toEqual([ ProductLine.security, ]); expect(mockStorage.set).toHaveBeenCalledWith(ACTIVE_PRODUCTS_STORAGE_KEY, [ @@ -47,13 +65,13 @@ describe('useStorage', () => { ]); mockStorage.set(ACTIVE_PRODUCTS_STORAGE_KEY, [ProductLine.security]); - expect(getStartedStorage.toggleActiveProductsInStorage(ProductLine.security)).toEqual([]); + expect(onboardingStorage.toggleActiveProductsInStorage(ProductLine.security)).toEqual([]); expect(mockStorage.set).toHaveBeenCalledWith(ACTIVE_PRODUCTS_STORAGE_KEY, []); }); it('should return the finished steps from storage by card ID', () => { expect( - getStartedStorage.getFinishedStepsFromStorageByCardId( + onboardingStorage.getFinishedStepsFromStorageByCardId( QuickStartSectionCardsId.createFirstProject ) ).toEqual([CreateProjectSteps.createFirstProject]); @@ -66,14 +84,14 @@ describe('useStorage', () => { }); expect( - getStartedStorage.getFinishedStepsFromStorageByCardId( + onboardingStorage.getFinishedStepsFromStorageByCardId( QuickStartSectionCardsId.createFirstProject ) ).toEqual([CreateProjectSteps.createFirstProject, 'step2']); }); it('should return all finished steps from storage', () => { - expect(getStartedStorage.getAllFinishedStepsFromStorage()).toEqual(defaultFinishedSteps); + expect(onboardingStorage.getAllFinishedStepsFromStorage()).toEqual(DEFAULT_FINISHED_STEPS); mockStorage.set(FINISHED_STEPS_STORAGE_KEY, { [QuickStartSectionCardsId.createFirstProject]: [ @@ -82,7 +100,7 @@ describe('useStorage', () => { ], [QuickStartSectionCardsId.watchTheOverviewVideo]: ['step3'], }); - expect(getStartedStorage.getAllFinishedStepsFromStorage()).toEqual({ + expect(onboardingStorage.getAllFinishedStepsFromStorage()).toEqual({ [QuickStartSectionCardsId.createFirstProject]: [ CreateProjectSteps.createFirstProject, 'step2', @@ -92,7 +110,7 @@ describe('useStorage', () => { }); it('should add a finished step to storage', () => { - getStartedStorage.addFinishedStepToStorage( + onboardingStorage.addFinishedStepToStorage( QuickStartSectionCardsId.createFirstProject, CreateProjectSteps.createFirstProject ); @@ -103,7 +121,7 @@ describe('useStorage', () => { mockStorage.set(FINISHED_STEPS_STORAGE_KEY, { [QuickStartSectionCardsId.createFirstProject]: [CreateProjectSteps.createFirstProject], }); - getStartedStorage.addFinishedStepToStorage( + onboardingStorage.addFinishedStepToStorage( QuickStartSectionCardsId.createFirstProject, 'step2' as StepId ); @@ -125,13 +143,13 @@ describe('useStorage', () => { }); expect( - getStartedStorage.getFinishedStepsFromStorageByCardId( + onboardingStorage.getFinishedStepsFromStorageByCardId( QuickStartSectionCardsId.createFirstProject ) ).toEqual([CreateProjectSteps.createFirstProject, 'step2']); expect( - getStartedStorage.getFinishedStepsFromStorageByCardId( + onboardingStorage.getFinishedStepsFromStorageByCardId( QuickStartSectionCardsId.watchTheOverviewVideo ) ).toEqual(['step3']); @@ -147,7 +165,7 @@ describe('useStorage', () => { card3: ['step4'], }); - expect(getStartedStorage.getAllFinishedStepsFromStorage()).toEqual({ + expect(onboardingStorage.getAllFinishedStepsFromStorage()).toEqual({ [QuickStartSectionCardsId.createFirstProject]: [ CreateProjectSteps.createFirstProject, 'step2', @@ -157,13 +175,14 @@ describe('useStorage', () => { }); mockStorage.set(FINISHED_STEPS_STORAGE_KEY, {}); - expect(getStartedStorage.getAllFinishedStepsFromStorage()).toEqual(defaultFinishedSteps); + expect(onboardingStorage.getAllFinishedStepsFromStorage()).toEqual(DEFAULT_FINISHED_STEPS); }); it('should remove a finished step from storage', () => { - getStartedStorage.removeFinishedStepFromStorage( + onboardingStorage.removeFinishedStepFromStorage( QuickStartSectionCardsId.createFirstProject, - 'step2' as StepId + 'step2' as StepId, + onboardingSteps ); expect(mockStorage.set).toHaveBeenCalledWith(FINISHED_STEPS_STORAGE_KEY, { [QuickStartSectionCardsId.createFirstProject]: [CreateProjectSteps.createFirstProject], @@ -178,9 +197,10 @@ describe('useStorage', () => { ], }); - getStartedStorage.removeFinishedStepFromStorage( + onboardingStorage.removeFinishedStepFromStorage( QuickStartSectionCardsId.createFirstProject, - CreateProjectSteps.createFirstProject + CreateProjectSteps.createFirstProject, + onboardingSteps ); expect(mockStorage.get(FINISHED_STEPS_STORAGE_KEY)).toEqual({ [QuickStartSectionCardsId.createFirstProject]: [ @@ -197,7 +217,7 @@ describe('useStorage', () => { expandedSteps: [CreateProjectSteps.createFirstProject], }, }); - const result = getStartedStorage.getAllExpandedCardStepsFromStorage(); + const result = onboardingStorage.getAllExpandedCardStepsFromStorage(); expect(mockStorage.get).toHaveBeenCalledWith(EXPANDED_CARDS_STORAGE_KEY); expect(result).toEqual({ [QuickStartSectionCardsId.createFirstProject]: { @@ -209,7 +229,7 @@ describe('useStorage', () => { it('should get default expanded card steps from storage', () => { (mockStorage.get as jest.Mock).mockReturnValueOnce(null); - const result = getStartedStorage.getAllExpandedCardStepsFromStorage(); + const result = onboardingStorage.getAllExpandedCardStepsFromStorage(); expect(mockStorage.get).toHaveBeenCalledWith(EXPANDED_CARDS_STORAGE_KEY); expect(result).toEqual(defaultExpandedCards); }); @@ -221,7 +241,7 @@ describe('useStorage', () => { expandedSteps: [OverviewSteps.getToKnowElasticSecurity], }, }); - getStartedStorage.resetAllExpandedCardStepsToStorage(); + onboardingStorage.resetAllExpandedCardStepsToStorage(); expect(mockStorage.get(EXPANDED_CARDS_STORAGE_KEY)).toEqual({ [QuickStartSectionCardsId.watchTheOverviewVideo]: { isExpanded: false, @@ -241,7 +261,7 @@ describe('useStorage', () => { expandedSteps: [OverviewSteps.getToKnowElasticSecurity], }, }); - getStartedStorage.addExpandedCardStepToStorage( + onboardingStorage.addExpandedCardStepToStorage( QuickStartSectionCardsId.watchTheOverviewVideo, OverviewSteps.getToKnowElasticSecurity ); @@ -264,7 +284,7 @@ describe('useStorage', () => { expandedSteps: [OverviewSteps.getToKnowElasticSecurity], }, }); - getStartedStorage.removeExpandedCardStepFromStorage( + onboardingStorage.removeExpandedCardStepFromStorage( QuickStartSectionCardsId.watchTheOverviewVideo, OverviewSteps.getToKnowElasticSecurity ); @@ -283,7 +303,7 @@ describe('useStorage', () => { expandedSteps: [CreateProjectSteps.createFirstProject], }, }); - getStartedStorage.removeExpandedCardStepFromStorage( + onboardingStorage.removeExpandedCardStepFromStorage( QuickStartSectionCardsId.createFirstProject ); expect(mockStorage.set).toHaveBeenCalledWith(EXPANDED_CARDS_STORAGE_KEY, { diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/storage.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/storage.ts similarity index 86% rename from x-pack/plugins/security_solution_serverless/public/get_started/storage.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/storage.ts index 661dab3a504b8..c7b036c7f294e 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/storage.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/storage.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { ProductLine } from '../../common/product'; import type { CardId, StepId } from './types'; import { @@ -14,9 +13,10 @@ import { GetStartedWithAlertsCardsId, } from './types'; -import { storage } from '../common/lib/storage'; -import { defaultFinishedSteps, isDefaultFinishedCardStep } from './helpers'; +import { DEFAULT_FINISHED_STEPS, isDefaultFinishedCardStep } from './helpers'; import { getSections } from './sections'; +import { storage } from '../../../lib/local_storage'; +import type { ProductLine } from './configs'; export const ACTIVE_PRODUCTS_STORAGE_KEY = 'securitySolution.getStarted.activeProducts'; export const FINISHED_STEPS_STORAGE_KEY = 'securitySolution.getStarted.finishedSteps'; @@ -31,10 +31,10 @@ export const defaultExpandedCards = { [GetStartedWithAlertsCardsId.viewAlerts]: { isExpanded: false, expandedSteps: [] }, }; -export const getStartedStorage = { +export const onboardingStorage = { setDefaultFinishedSteps: (cardId: CardId) => { const allFinishedSteps: Record = storage.get(FINISHED_STEPS_STORAGE_KEY); - const defaultFinishedStepsByCardId = defaultFinishedSteps[cardId]; + const defaultFinishedStepsByCardId = DEFAULT_FINISHED_STEPS[cardId]; const hasDefaultFinishedSteps = defaultFinishedStepsByCardId != null; if (!hasDefaultFinishedSteps) { return; @@ -64,18 +64,18 @@ export const getStartedStorage = { return activeProducts; }, getFinishedStepsFromStorageByCardId: (cardId: CardId) => { - const finishedSteps = getStartedStorage.getAllFinishedStepsFromStorage(); + const finishedSteps = onboardingStorage.getAllFinishedStepsFromStorage(); const steps: StepId[] = finishedSteps[cardId] ?? []; return steps; }, getAllFinishedStepsFromStorage: () => { const allFinishedSteps: Record = storage.get(FINISHED_STEPS_STORAGE_KEY); if (allFinishedSteps == null) { - storage.set(FINISHED_STEPS_STORAGE_KEY, defaultFinishedSteps); + storage.set(FINISHED_STEPS_STORAGE_KEY, DEFAULT_FINISHED_STEPS); } else { getSections().forEach((section) => { section.cards?.forEach((card) => { - getStartedStorage.setDefaultFinishedSteps(card.id); + onboardingStorage.setDefaultFinishedSteps(card.id); }); }); } @@ -83,18 +83,18 @@ export const getStartedStorage = { }, addFinishedStepToStorage: (cardId: CardId, stepId: StepId) => { - const finishedSteps = getStartedStorage.getAllFinishedStepsFromStorage(); + const finishedSteps = onboardingStorage.getAllFinishedStepsFromStorage(); const card: StepId[] = finishedSteps[cardId] ?? []; if (card.indexOf(stepId) < 0) { card.push(stepId); storage.set(FINISHED_STEPS_STORAGE_KEY, { ...finishedSteps, [cardId]: card }); } }, - removeFinishedStepFromStorage: (cardId: CardId, stepId: StepId) => { - if (isDefaultFinishedCardStep(cardId, stepId)) { + removeFinishedStepFromStorage: (cardId: CardId, stepId: StepId, onboardingSteps: StepId[]) => { + if (isDefaultFinishedCardStep(cardId, stepId, onboardingSteps)) { return; } - const finishedSteps = getStartedStorage.getAllFinishedStepsFromStorage(); + const finishedSteps = onboardingStorage.getAllFinishedStepsFromStorage(); const steps: StepId[] = finishedSteps[cardId] ?? []; const index = steps.indexOf(stepId); if (index >= 0) { @@ -111,7 +111,7 @@ export const getStartedStorage = { }, resetAllExpandedCardStepsToStorage: () => { const activeCards: Record = - getStartedStorage.getAllExpandedCardStepsFromStorage(); + onboardingStorage.getAllExpandedCardStepsFromStorage(); storage.set( EXPANDED_CARDS_STORAGE_KEY, @@ -123,7 +123,7 @@ export const getStartedStorage = { }, addExpandedCardStepToStorage: (cardId: CardId, stepId: StepId) => { const activeCards: Record = - getStartedStorage.getAllExpandedCardStepsFromStorage(); + onboardingStorage.getAllExpandedCardStepsFromStorage(); const card = activeCards[cardId] ? { expandedSteps: [stepId], diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/styles/add_integrations_callout.styles.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/add_integrations_callout.styles.ts similarity index 56% rename from x-pack/plugins/security_solution_serverless/public/get_started/styles/add_integrations_callout.styles.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/add_integrations_callout.styles.ts index b4bb194acd65d..a99db6142d9fe 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/styles/add_integrations_callout.styles.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/add_integrations_callout.styles.ts @@ -6,7 +6,7 @@ */ import { useEuiBackgroundColor, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; +import { css } from '@emotion/css'; import { useMemo } from 'react'; export const useAddIntegrationsCalloutStyles = () => { @@ -15,23 +15,21 @@ export const useAddIntegrationsCalloutStyles = () => { const customStyles = useMemo( () => ({ - calloutWrapperStyles: css` - border-radius: ${euiTheme.border.radius.medium}; - border: 1px solid ${euiTheme.colors.lightShade}; - padding: ${euiTheme.size.xs} ${euiTheme.size.m}; - background-color: ${backgroundColor}; - margin-top: ${euiTheme.size.base}; - `, - calloutTitleStyles: css` - color: ${euiTheme.colors.title}; - font-size: ${euiTheme.size.m}; - font-weight: ${euiTheme.font.weight.regular}; - line-height: ${euiTheme.base * 1.25}px; - margin-left: ${euiTheme.size.xs}; - `, - calloutAnchorStyles: css` - margin-left: ${euiTheme.size.s}; - `, + calloutWrapperStyles: css({ + borderRadius: euiTheme.border.radius.medium, + border: `1px solid ${euiTheme.colors.lightShade}`, + padding: `${euiTheme.size.xs} ${euiTheme.size.m}`, + backgroundColor, + marginTop: euiTheme.size.base, + }), + calloutTitleStyles: css({ + color: euiTheme.colors.title, + fontSize: euiTheme.size.m, + fontWeight: euiTheme.font.weight.regular, + lineHeight: `${euiTheme.base * 1.25}px`, + marginLeft: euiTheme.size.xs, + }), + calloutAnchorStyles: css({ marginLeft: euiTheme.size.s }), }), [ backgroundColor, diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/styles/card_item.styles.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/card_item.styles.ts similarity index 51% rename from x-pack/plugins/security_solution_serverless/public/get_started/styles/card_item.styles.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/card_item.styles.ts index aadf1010db9f7..82b2a38b07288 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/styles/card_item.styles.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/card_item.styles.ts @@ -6,7 +6,7 @@ */ import { useEuiShadow, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; +import { css } from '@emotion/css'; export const SHADOW_ANIMATION_DURATION = 350; @@ -14,22 +14,19 @@ export const useCardItemStyles = () => { const { euiTheme } = useEuiTheme(); const shadow = useEuiShadow('l'); - return css` - &.card-item { - padding: ${euiTheme.size.base}; - border-radius: ${euiTheme.size.s}; - border: 1px solid ${euiTheme.colors.lightShade}; - box-sizing: content-box; - - &:hover, - &.card-expanded { - ${shadow}; - transition: box-shadow ${SHADOW_ANIMATION_DURATION}ms ease-out; - } - - &.card-expanded { - border: 2px solid #6092c0; - } - } - `; + return css({ + '&.card-item': { + padding: euiTheme.size.base, + borderRadius: euiTheme.size.s, + border: `1px solid ${euiTheme.colors.lightShade}`, + boxSizing: `content-box`, + }, + '&:hover, &.card-expanded': { + boxShadow: shadow, + transition: `box-shadow ${SHADOW_ANIMATION_DURATION}ms ease-out`, + }, + '&.card-expanded': { + border: `2px solid #6092c0`, + }, + }); }; diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/card_step.styles.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/card_step.styles.ts new file mode 100644 index 0000000000000..6180e6d914df9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/card_step.styles.ts @@ -0,0 +1,88 @@ +/* + * 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 { useEuiBackgroundColor, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/css'; +import { useMemo } from 'react'; + +export const HEIGHT_ANIMATION_DURATION = 250; + +export const useCardStepStyles = () => { + const { euiTheme } = useEuiTheme(); + const completeStepBackgroundColor = useEuiBackgroundColor('success'); + + const customStyles = useMemo( + () => ({ + stepPanelStyles: css({ + '.stepContentWrapper': { + display: 'grid', + gridTemplateRows: '1fr', + transition: `grid-template-rows ${HEIGHT_ANIMATION_DURATION}ms ease-in`, + }, + + '&.step-panel-collapsed .stepContentWrapper': { + gridTemplateRows: '0fr', + }, + + '.stepContent': { + overflow: 'hidden', + }, + }), + getStepGroundStyles: ({ hasStepContent }: { hasStepContent: boolean }) => + css({ + cursor: hasStepContent ? 'pointer' : 'default', + gap: euiTheme.size.base, + }), + stepItemStyles: css({ alignSelf: 'center' }), + stepIconStyles: css({ + '&.step-icon': { + borderRadius: '50%', + width: euiTheme.size.xxxl, + height: euiTheme.size.xxxl, + padding: euiTheme.size.m, + backgroundColor: euiTheme.colors.body, + }, + + '&.step-icon-done': { + backgroundColor: completeStepBackgroundColor, + }, + }), + stepTitleStyles: css({ + '&.step-title': { + paddingRight: euiTheme.size.m, + lineHeight: euiTheme.size.xxxl, + fontSize: `${euiTheme.base * 0.875}px`, + fontWeight: euiTheme.font.weight.semiBold, + verticalAlign: 'middle', + }, + }), + allDoneTextStyles: css({ + '&.all-done-badge': { + backgroundColor: completeStepBackgroundColor, + color: euiTheme.colors.successText, + }, + }), + toggleButtonStyles: css({ + '&.toggle-button': { + marginLeft: `${euiTheme.base * 0.375}px`, + }, + }), + }), + [ + completeStepBackgroundColor, + euiTheme.base, + euiTheme.colors.body, + euiTheme.colors.successText, + euiTheme.font.weight.semiBold, + euiTheme.size.base, + euiTheme.size.m, + euiTheme.size.xxxl, + ] + ); + + return customStyles; +}; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/styles/footer.styles.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/footer.styles.ts similarity index 50% rename from x-pack/plugins/security_solution_serverless/public/get_started/styles/footer.styles.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/footer.styles.ts index 2b2d8ef144610..37185821f4644 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/styles/footer.styles.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/footer.styles.ts @@ -6,7 +6,7 @@ */ import { useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; +import { css } from '@emotion/css'; import { useMemo } from 'react'; export const useFooterStyles = () => { @@ -14,27 +14,27 @@ export const useFooterStyles = () => { const footerStyles = useMemo( () => ({ - wrapperStyle: css` - padding: ${euiTheme.size.xl} ${euiTheme.size.l} ${euiTheme.base * 4.5}px; - gap: ${euiTheme.base * 3.75}px; - `, - titleStyle: css` - font-size: ${euiTheme.base * 0.875}px; - font-weight: ${euiTheme.font.weight.semiBold}; - line-height: ${euiTheme.size.l}; - color: ${euiTheme.colors.title}; - `, - descriptionStyle: css` - font-size: 12.25px; - font-weight: ${euiTheme.font.weight.regular}; - line-height: ${euiTheme.base * 1.25}px; - color: ${euiTheme.colors.darkestShade}; - `, - linkStyle: css` - font-size: ${euiTheme.size.m}; - font-weight: ${euiTheme.font.weight.medium}; - line-height: ${euiTheme.size.base}; - `, + wrapperStyle: css({ + padding: `${euiTheme.size.xl} ${euiTheme.size.l} ${euiTheme.base * 4.5}px`, + gap: `${euiTheme.base * 3.75}px`, + }), + titleStyle: css({ + fontSize: `${euiTheme.base * 0.875}px`, + fontWeight: euiTheme.font.weight.semiBold, + lineHeight: euiTheme.size.l, + color: euiTheme.colors.title, + }), + descriptionStyle: css({ + fontSize: '12.25px', + fontWeight: euiTheme.font.weight.regular, + lineHeight: `${euiTheme.base * 1.25}px`, + color: euiTheme.colors.darkestShade, + }), + linkStyle: css({ + fontSize: euiTheme.size.m, + fontWeight: euiTheme.font.weight.medium, + lineHeight: euiTheme.size.base, + }), }), [ euiTheme.base, diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/onboarding.styles.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/onboarding.styles.ts new file mode 100644 index 0000000000000..81cc81fda8818 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/onboarding.styles.ts @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/css'; + +export const useOnboardingStyles = () => { + const { euiTheme } = useEuiTheme(); + + return { + wrapperStyles: css({ + margin: `0 -${euiTheme.size.l}`, + }), + progressSectionStyles: css({ + backgroundColor: euiTheme.colors.lightestShade, + padding: `${euiTheme.size.xxl} ${euiTheme.size.xxl} ${euiTheme.size.m}`, + }), + stepsSectionStyles: css({ + padding: `0 ${euiTheme.size.xxl} ${euiTheme.size.xxxl}`, + backgroundColor: euiTheme.colors.lightestShade, + }), + }; +}; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/styles/progress_bar.style.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/progress_bar.style.ts similarity index 74% rename from x-pack/plugins/security_solution_serverless/public/get_started/styles/progress_bar.style.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/progress_bar.style.ts index f776acfcfb848..466d4dfaa8dc8 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/styles/progress_bar.style.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/progress_bar.style.ts @@ -6,18 +6,18 @@ */ import { useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; +import { css } from '@emotion/css'; import { useMemo } from 'react'; export const useProgressBarStyles = () => { const { euiTheme } = useEuiTheme(); const progressBarStyles = useMemo( () => ({ - textStyle: css` - font-size: 10.5px; - font-weight: ${euiTheme.font.weight.bold}; - text-transform: uppercase; - `, + textStyle: css({ + fontSize: '10.5px', + fontWeight: euiTheme.font.weight.bold, + textTransform: 'uppercase', + }), }), [euiTheme.font.weight.bold] ); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/step_content.styles.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/step_content.styles.ts new file mode 100644 index 0000000000000..6b4c75e7e8d89 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/step_content.styles.ts @@ -0,0 +1,87 @@ +/* + * 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 { useEuiShadow, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/css'; +import { useMemo } from 'react'; + +export const LEFT_CONTENT_PANEL_WIDTH = 486; +export const RIGHT_CONTENT_PANEL_WIDTH = 510; +export const RIGHT_CONTENT_HEIGHT = 320; +export const RIGHT_CONTENT_WIDTH = 480; + +export const useStepContentStyles = () => { + const { euiTheme } = useEuiTheme(); + const imageShadow = useEuiShadow('s'); + + const customStyles = useMemo( + () => ({ + stepContentGroupStyles: css({ + '&.step-content-group': { + justifyContent: 'space-between', + marginTop: euiTheme.size.l, + padding: euiTheme.size.l, + transition: `opacity ${euiTheme.animation.normal}`, + overflow: 'hidden', + border: `1px solid ${euiTheme.colors.lightShade}`, + borderRadius: euiTheme.border.radius.medium, + }, + }), + leftContentStyles: css({ + '&.left-panel': { + padding: `0 0 0 ${euiTheme.size.s}`, + width: `${LEFT_CONTENT_PANEL_WIDTH}px`, + }, + }), + descriptionStyles: css({ + '&.step-content-description': { + marginBottom: '0px', + marginBlockEnd: '0px !important', + lineHeight: euiTheme.size.l, + + '.step-paragraph': { + marginTop: euiTheme.size.xl, + }, + }, + }), + rightPanelStyles: css({ + '&.right-panel': { + padding: `0 6px 0 ${euiTheme.size.l}`, + width: `${RIGHT_CONTENT_PANEL_WIDTH}px`, + }, + }), + rightPanelContentStyles: css({ + '&.right-panel-wrapper': { + height: `${RIGHT_CONTENT_HEIGHT}px`, + width: `${RIGHT_CONTENT_WIDTH}px`, + }, + }), + getRightContentStyles: ({ shadow }: { shadow: boolean }) => + css({ + '&.right-panel-content': { + height: '100%', + width: '100%', + position: 'relative', + overflow: 'hidden', + boxShadow: shadow ? imageShadow : '', + borderRadius: euiTheme.border.radius.medium, + }, + }), + }), + [ + euiTheme.animation.normal, + euiTheme.border.radius.medium, + euiTheme.colors.lightShade, + euiTheme.size.l, + euiTheme.size.s, + euiTheme.size.xl, + imageShadow, + ] + ); + + return customStyles; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/welcome_header.styles.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/welcome_header.styles.ts new file mode 100644 index 0000000000000..ef50739d4f7e9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/welcome_header.styles.ts @@ -0,0 +1,78 @@ +/* + * 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 { useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/css'; +import { useMemo } from 'react'; +import { CONTENT_WIDTH } from '../helpers'; +import launch from '../images/launch.png'; + +export const useWelcomeHeaderStyles = () => { + const { euiTheme } = useEuiTheme(); + + const welcomeHeaderStyles = useMemo(() => { + return { + headerStyles: css({ + backgroundImage: `url(${launch})`, + backgroundSize: '40%', + backgroundRepeat: 'no-repeat', + backgroundPositionX: 'right', + backgroundPositionY: 'center', + padding: `${euiTheme.base * 0.625}px 0`, + }), + headerTitleStyles: css({ + paddingBottom: euiTheme.size.s, + fontSize: `${euiTheme.base}px`, + color: euiTheme.colors.darkShade, + fontWeight: euiTheme.font.weight.bold, + lineHeight: euiTheme.size.l, + }), + headerSubtitleStyles: css({ + fontSize: `${euiTheme.base * 2.125}px`, + color: euiTheme.colors.title, + fontWeight: euiTheme.font.weight.bold, + }), + headerDescriptionStyles: css({ + fontSize: `${euiTheme.base}px`, + color: euiTheme.colors.subduedText, + lineHeight: euiTheme.size.l, + fontWeight: euiTheme.font.weight.regular, + }), + headerContentStyles: css({ + width: `${CONTENT_WIDTH / 2}px`, + }), + currentPlanWrapperStyles: css({ + backgroundColor: euiTheme.colors.lightestShade, + borderRadius: '56px', + padding: `${euiTheme.size.xs} ${euiTheme.size.s} ${euiTheme.size.xs} ${euiTheme.size.m}`, + height: euiTheme.size.xl, + }), + currentPlanTextStyles: css({ + fontSize: euiTheme.size.m, + fontWeight: euiTheme.font.weight.bold, + paddingRight: euiTheme.size.xs, + }), + projectFeaturesUrlStyles: css({ + paddingLeft: euiTheme.size.xs, + }), + }; + }, [ + euiTheme.base, + euiTheme.colors.darkShade, + euiTheme.colors.lightestShade, + euiTheme.colors.subduedText, + euiTheme.colors.title, + euiTheme.font.weight.bold, + euiTheme.font.weight.regular, + euiTheme.size.l, + euiTheme.size.m, + euiTheme.size.s, + euiTheme.size.xl, + euiTheme.size.xs, + ]); + return welcomeHeaderStyles; +}; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/toggle_panel.test.tsx similarity index 97% rename from x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/toggle_panel.test.tsx index a0f0b1fa88f24..8e1566a38b880 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/toggle_panel.test.tsx @@ -10,8 +10,7 @@ import { TogglePanel } from './toggle_panel'; import { useSetUpSections } from './hooks/use_setup_sections'; import type { ActiveSections } from './types'; import { QuickStartSectionCardsId, SectionId } from './types'; - -import { ProductLine } from '../../common/product'; +import { ProductLine } from './configs'; jest.mock('@elastic/eui', () => ({ ...jest.requireActual('@elastic/eui'), diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/toggle_panel.tsx similarity index 94% rename from x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/toggle_panel.tsx index c0d2d94023921..45b18d046a601 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/toggle_panel.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/toggle_panel.tsx @@ -8,14 +8,14 @@ import React from 'react'; import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; +import { css } from '@emotion/css'; import * as i18n from './translations'; import { useSetUpSections } from './hooks/use_setup_sections'; import type { ActiveSections } from './types'; -import type { ProductLine } from '../../common/product'; import { useStepContext } from './context/step_context'; +import type { ProductLine } from './configs'; const TogglePanelComponent: React.FC<{ activeProducts: Set; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/translations.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/translations.ts similarity index 66% rename from x-pack/plugins/security_solution_serverless/public/get_started/translations.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/translations.ts index 10a589f19020c..721c5300e9adc 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/translations.ts @@ -8,88 +8,88 @@ import { i18n } from '@kbn/i18n'; export const GET_STARTED_PAGE_TITLE = (userName: string) => - i18n.translate('xpack.securitySolutionServerless.getStarted.Title', { + i18n.translate('xpack.securitySolution.onboarding.Title', { defaultMessage: `Hi {userName}!`, values: { userName }, }); export const GET_STARTED_PAGE_SUBTITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.subTitle', + 'xpack.securitySolution.onboarding.subTitle', { defaultMessage: `Get started with Security`, } ); export const GET_STARTED_PAGE_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.description', + 'xpack.securitySolution.onboarding.description', { defaultMessage: `This area shows you everything you need to know. Feel free to explore all content. You can always come back later at any time.`, } ); export const CURRENT_PLAN_LABEL = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.currentPlan.label', + 'xpack.securitySolution.onboarding.currentPlan.label', { defaultMessage: 'Current plan:', } ); export const PROGRESS_TRACKER_LABEL = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.progressTracker.progressBar.label', + 'xpack.securitySolution.onboarding.progressTracker.progressBar.label', { defaultMessage: 'PROGRESS' } ); export const SECTION_1_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.section1.title', + 'xpack.securitySolution.onboarding.togglePanel.section1.title', { defaultMessage: 'Quick start', } ); export const SECTION_2_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.section2.title', + 'xpack.securitySolution.onboarding.togglePanel.section2.title', { defaultMessage: 'Add and validate your data', } ); export const SECTION_3_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.section3.title', + 'xpack.securitySolution.onboarding.togglePanel.section3.title', { defaultMessage: 'Get started with alerts', } ); export const CREATE_PROJECT_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.createProject.title', + 'xpack.securitySolution.onboarding.step.createProject.title', { defaultMessage: 'Create your first project', } ); export const CREATE_PROJECT_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.createProject.description', + 'xpack.securitySolution.onboarding.step.createProject.description', { defaultMessage: `Create Elastic Security project with our fully-managed serverless solutions that automatically manage nodes, shards, data tiers and scaling to maintain the health and performance so you can focus on your data and goals.`, } ); export const WATCH_VIDEO_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.watchVideo.title', + 'xpack.securitySolution.onboarding.step.watchVideo.title', { defaultMessage: 'Watch the overview video', } ); export const WATCH_VIDEO_BUTTON_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.watchVideo.button.title', + 'xpack.securitySolution.onboarding.step.watchVideo.button.title', { defaultMessage: 'Elastic Security', } ); export const WATCH_VIDEO_DESCRIPTION1 = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.watchVideo.description1', + 'xpack.securitySolution.onboarding.step.watchVideo.description1', { defaultMessage: `Elastic Security unifies analytics, EDR, cloud security capabilities, and more into a SaaS solution that helps you improve your organization’s security posture, defend against a wide range of threats, and prevent breaches. `, @@ -97,21 +97,21 @@ export const WATCH_VIDEO_DESCRIPTION1 = i18n.translate( ); export const WATCH_VIDEO_DESCRIPTION2 = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.watchVideo.description2', + 'xpack.securitySolution.onboarding.step.watchVideo.description2', { defaultMessage: `To explore the platform’s core features, watch the video:`, } ); export const ADD_INTEGRATIONS_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.addIntegrations.title', + 'xpack.securitySolution.onboarding.step.addIntegrations.title', { defaultMessage: 'Add integrations', } ); export const ADD_INTEGRATIONS_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.addIntegrations.description', + 'xpack.securitySolution.onboarding.step.addIntegrations.description', { defaultMessage: 'Use third-party integrations to import data from common sources and help you gather relevant information in one place. To find integrations for your use case, search for tools and data providers on the Add integrations page.', @@ -119,21 +119,21 @@ export const ADD_INTEGRATIONS_DESCRIPTION = i18n.translate( ); export const ADD_INTEGRATIONS_IMAGE_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.addIntegrations.image.title', + 'xpack.securitySolution.onboarding.step.addIntegrations.image.title', { defaultMessage: 'Connect to existing data sources', } ); export const VIEW_DASHBOARDS = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.viewDashboards.title', + 'xpack.securitySolution.onboarding.step.viewDashboards.title', { defaultMessage: 'View and analyze your data using dashboards', } ); export const VIEW_DASHBOARDS_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.viewDashboards.description', + 'xpack.securitySolution.onboarding.step.viewDashboards.description', { defaultMessage: 'Use dashboards to visualize data and stay up-to-date with key information. Create your own, or use Elastic’s default dashboards — including alerts, user authentication events, known vulnerabilities, and more.', @@ -141,21 +141,21 @@ export const VIEW_DASHBOARDS_DESCRIPTION = i18n.translate( ); export const VIEW_DASHBOARDS_IMAGE_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.viewDashboards.image.title', + 'xpack.securitySolution.onboarding.step.viewDashboards.image.title', { defaultMessage: 'Analyze data using dashboards', } ); export const ENABLE_RULES = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.enableRules.title', + 'xpack.securitySolution.onboarding.step.enableRules.title', { defaultMessage: 'Enable prebuilt rules', } ); export const ENABLE_RULES_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.enableRules.description', + 'xpack.securitySolution.onboarding.step.enableRules.description', { defaultMessage: 'Elastic Security comes with prebuilt detection rules that run in the background and create alerts when their conditions are met.', @@ -163,14 +163,14 @@ export const ENABLE_RULES_DESCRIPTION = i18n.translate( ); export const VIEW_ALERTS_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.viewAlerts.title', + 'xpack.securitySolution.onboarding.step.viewAlerts.title', { defaultMessage: 'View alerts', } ); export const VIEW_ALERTS_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.step.viewAlerts.description', + 'xpack.securitySolution.onboarding.step.viewAlerts.description', { defaultMessage: 'Visualize, sort, filter, and investigate alerts from across your infrastructure. Examine individual alerts of interest, and discover general patterns in alert volume and severity.', @@ -178,42 +178,42 @@ export const VIEW_ALERTS_DESCRIPTION = i18n.translate( ); export const PRODUCT_BADGE_ANALYTICS = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.productBadge.analytics', + 'xpack.securitySolution.onboarding.togglePanel.productBadge.analytics', { defaultMessage: 'Analytics', } ); export const PRODUCT_BADGE_CLOUD = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.productBadge.cloud', + 'xpack.securitySolution.onboarding.togglePanel.productBadge.cloud', { defaultMessage: 'Cloud', } ); export const PRODUCT_BADGE_EDR = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.productBadge.edr', + 'xpack.securitySolution.onboarding.togglePanel.productBadge.edr', { defaultMessage: 'EDR', } ); export const TOGGLE_PANEL_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStartedProductLabel.title', + 'xpack.securitySolution.onboardingProductLabel.title', { defaultMessage: `Curate your Get Started experience:`, } ); export const ANALYTICS_SWITCH_LABEL = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.switch.analytics.label', + 'xpack.securitySolution.onboarding.togglePanel.switch.analytics.label', { defaultMessage: 'Analytics', } ); export const CLOUD_SWITCH_LABEL = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.switch.cloud.label', + 'xpack.securitySolution.onboarding.togglePanel.switch.cloud.label', { defaultMessage: 'Cloud Security', @@ -221,56 +221,56 @@ export const CLOUD_SWITCH_LABEL = i18n.translate( ); export const ENDPOINT_SWITCH_LABEL = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.switch.endpoint.label', + 'xpack.securitySolution.onboarding.togglePanel.switch.endpoint.label', { defaultMessage: 'Endpoint Security', } ); export const MARK_AS_DONE_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.markAsDoneTitle', + 'xpack.securitySolution.onboarding.togglePanel.markAsDoneTitle', { defaultMessage: 'Mark as done', } ); export const UNDO_MARK_AS_DONE_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.undoMarkAsDoneTitle', + 'xpack.securitySolution.onboarding.togglePanel.undoMarkAsDoneTitle', { defaultMessage: `Undo 'mark as done'`, } ); export const TOGGLE_PANEL_EMPTY_TITLE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.empty.title', + 'xpack.securitySolution.onboarding.togglePanel.empty.title', { defaultMessage: `Hmm, there doesn't seem to be anything there`, } ); export const TOGGLE_PANEL_EMPTY_DESCRIPTION = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.empty.description', + 'xpack.securitySolution.onboarding.togglePanel.empty.description', { defaultMessage: `Switch on a toggle to continue your curated "Get Started" experience`, } ); export const ALL_DONE_TEXT = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.done.title', + 'xpack.securitySolution.onboarding.togglePanel.done.title', { defaultMessage: 'Step complete', } ); export const COLLAPSE_STEP_BUTTON_LABEL = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.collapseStepButton.label', + 'xpack.securitySolution.onboarding.togglePanel.collapseStepButton.label', { defaultMessage: 'Collapse', } ); export const EXPAND_STEP_BUTTON_LABEL = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.togglePanel.expandStepButton.label', + 'xpack.securitySolution.onboarding.togglePanel.expandStepButton.label', { defaultMessage: 'Expand', } diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/types.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/types.ts similarity index 94% rename from x-pack/plugins/security_solution_serverless/public/get_started/types.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/types.ts index 30504913e8aff..15f938113e76f 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/types.ts @@ -9,8 +9,7 @@ import type { EuiIconProps } from '@elastic/eui'; import type React from 'react'; import type { MutableRefObject } from 'react'; import type { HttpSetup } from '@kbn/core/public'; - -import type { ProductLine } from '../../common/product'; +import type { ProductLine } from './configs'; export interface Section { cards: Card[]; @@ -148,25 +147,26 @@ export interface TogglePanelReducer { finishedSteps: Record>; totalActiveSteps: number | null; totalStepsLeft: number | null; + onboardingSteps: StepId[]; } export interface ToggleProductAction { - type: GetStartedPageActions.ToggleProduct; + type: OnboardingActions.ToggleProduct; payload: { section: ProductLine }; } export interface AddFinishedStepAction { - type: GetStartedPageActions.AddFinishedStep; + type: OnboardingActions.AddFinishedStep; payload: { stepId: StepId; cardId: CardId; sectionId: SectionId }; } export interface RemoveFinishedStepAction { - type: GetStartedPageActions.RemoveFinishedStep; + type: OnboardingActions.RemoveFinishedStep; payload: { stepId: StepId; cardId: CardId; sectionId: SectionId }; } export interface ToggleStepAction { - type: GetStartedPageActions.ToggleExpandedStep; + type: OnboardingActions.ToggleExpandedStep; payload: { stepId: StepId; cardId: CardId; isStepExpanded?: boolean }; } @@ -181,7 +181,7 @@ export interface Switch { label: string; } -export enum GetStartedPageActions { +export enum OnboardingActions { AddFinishedStep = 'addFinishedStep', RemoveFinishedStep = 'removeFinishedStep', ToggleProduct = 'toggleProduct', diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.tsx new file mode 100644 index 0000000000000..e33860d5408ee --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.tsx @@ -0,0 +1,84 @@ +/* + * 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 { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; +import React from 'react'; + +import classnames from 'classnames'; +import { + GET_STARTED_PAGE_TITLE, + GET_STARTED_PAGE_SUBTITLE, + GET_STARTED_PAGE_DESCRIPTION, + CURRENT_PLAN_LABEL, +} from '../translations'; +import { ProductTierBadge } from './product_tier_badge'; +import { useWelcomeHeaderStyles } from '../styles/welcome_header.styles'; +import type { ProductTier } from '../configs'; +import { useProjectFeaturesUrl } from '../hooks/use_project_features_url'; +import { useCurrentUser } from '../../../../lib/kibana'; + +const WelcomeHeaderComponent: React.FC<{ productTier?: ProductTier }> = ({ productTier }) => { + const userName = useCurrentUser(); + const projectFeaturesUrl = useProjectFeaturesUrl(); + const { + headerContentStyles, + headerStyles, + headerTitleStyles, + headerSubtitleStyles, + headerDescriptionStyles, + currentPlanWrapperStyles, + currentPlanTextStyles, + projectFeaturesUrlStyles, + } = useWelcomeHeaderStyles(); + + const headerSubtitleClassNames = classnames('eui-displayBlock', headerSubtitleStyles); + const headerDescriptionClassNames = classnames('eui-displayBlock', headerDescriptionStyles); + const currentPlanWrapperClassNames = classnames( + 'eui-displayInlineBlock', + currentPlanWrapperStyles + ); + const projectFeaturesUrlClassNames = classnames('eui-alignMiddle', projectFeaturesUrlStyles); + + return ( + + + {userName?.username && ( + + {GET_STARTED_PAGE_TITLE(userName.username)} + + )} + + {GET_STARTED_PAGE_SUBTITLE} + + {GET_STARTED_PAGE_DESCRIPTION} + {productTier && projectFeaturesUrl && ( + <> + +
+
+ {CURRENT_PLAN_LABEL} + + + +
+
+ + )} +
+
+ ); +}; + +export const WelcomeHeader = React.memo(WelcomeHeaderComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/product_tier_badge.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/product_tier_badge.test.tsx similarity index 95% rename from x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/product_tier_badge.test.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/product_tier_badge.test.tsx index e739d2cb95ea5..ed15685f8cf59 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/product_tier_badge.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/product_tier_badge.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { ProductTierBadge } from './product_tier_badge'; -import { ProductTier } from '../../../common/product'; +import { ProductTier } from '../configs'; describe('ProductTierBadge', () => { it('renders nothing when productTier is undefined', () => { diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/product_tier_badge.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/product_tier_badge.tsx similarity index 92% rename from x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/product_tier_badge.tsx rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/product_tier_badge.tsx index 559db32d83cf5..a0df046d89406 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/product_tier_badge.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/product_tier_badge.tsx @@ -7,9 +7,9 @@ import React from 'react'; import { EuiBadge, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { ProductTier } from '../../../common/product'; +import { css } from '@emotion/css'; import { PRODUCT_TIER_ESSENTIAL, PRODUCT_TIER_COMPLETE } from './translations'; +import { ProductTier } from '../configs'; const ProductTierBadgeComponent = ({ productTier }: { productTier: ProductTier | undefined }) => { const { euiTheme } = useEuiTheme(); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/translations.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/translations.ts similarity index 74% rename from x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/translations.ts rename to x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/translations.ts index bef05ca52e9df..3406f5670b99a 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/translations.ts @@ -7,14 +7,14 @@ import { i18n } from '@kbn/i18n'; export const PRODUCT_TIER_ESSENTIAL = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.welcomePanel.productTier.essential', + 'xpack.securitySolution.onboarding.welcomePanel.productTier.essential', { defaultMessage: `Essential`, } ); export const PRODUCT_TIER_COMPLETE = i18n.translate( - 'xpack.securitySolutionServerless.getStarted.welcomePanel.productTier.complete', + 'xpack.securitySolution.onboarding.welcomePanel.productTier.complete', { defaultMessage: `Complete`, } diff --git a/x-pack/plugins/security_solution/public/common/lib/local_storage/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/lib/local_storage/__mocks__/index.ts new file mode 100644 index 0000000000000..719d9c054f67d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/lib/local_storage/__mocks__/index.ts @@ -0,0 +1,26 @@ +/* + * 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. + */ + +export interface MockStorage { + data: Record; + clearMockStorageData: () => void; + get: (key: string) => unknown; + set: (key: string, value: unknown) => void; +} + +const mockStorage: MockStorage = { + data: {}, + clearMockStorageData: () => { + mockStorage.data = {}; + }, + get: jest.fn((key: string) => mockStorage.data[key]), + set: jest.fn((key, value) => { + mockStorage.data[key] = value; + }), +}; + +export const storage = mockStorage; diff --git a/x-pack/plugins/security_solution/public/common/lib/local_storage/index.ts b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.ts new file mode 100644 index 0000000000000..f315bea6a3d9a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { Storage } from '@kbn/kibana-utils-plugin/public'; + +export const storage = new Storage(localStorage); diff --git a/x-pack/plugins/security_solution/public/index.ts b/x-pack/plugins/security_solution/public/index.ts index 92fcf14ace691..61c56ad5a36c5 100644 --- a/x-pack/plugins/security_solution/public/index.ts +++ b/x-pack/plugins/security_solution/public/index.ts @@ -16,3 +16,12 @@ export const plugin = (context: PluginInitializerContext): Plugin => new Plugin( export type { PluginSetup, PluginStart }; export { Plugin }; + +export { + CreateProjectSteps, + OverviewSteps, + AddIntegrationsSteps, + ViewDashboardSteps, + EnablePrebuiltRulesSteps, + ViewAlertsSteps, +} from './common/components/landing_page/onboarding/types'; diff --git a/x-pack/plugins/security_solution/public/mocks.ts b/x-pack/plugins/security_solution/public/mocks.ts index 6564697c95f08..fcb78b1c735c7 100644 --- a/x-pack/plugins/security_solution/public/mocks.ts +++ b/x-pack/plugins/security_solution/public/mocks.ts @@ -11,13 +11,16 @@ import type { BreadcrumbsNav } from './common/breadcrumbs'; import type { NavigationLink } from './common/links/types'; import { allowedExperimentalValues } from '../common/experimental_features'; import type { PluginStart, PluginSetup, ContractStartServices } from './types'; +import { OnboardingPageService } from './app/components/onboarding/onboarding_page_service'; const upselling = new UpsellingService(); +const onboardingPageService = new OnboardingPageService(); export const contractStartServicesMock: ContractStartServices = { extraRoutes$: of([]), getComponents$: jest.fn(() => of({})), upselling, + onboarding: onboardingPageService, }; const setupMock = (): PluginSetup => ({ @@ -35,6 +38,7 @@ const startMock = (): PluginStart => ({ ), setExtraRoutes: jest.fn(), getUpselling: () => upselling, + setOnboardingPageSettings: onboardingPageService, }); export const securitySolutionMock = { diff --git a/x-pack/plugins/security_solution/public/plugin_contract.ts b/x-pack/plugins/security_solution/public/plugin_contract.ts index f7e109c94af84..4b4ba256a81ea 100644 --- a/x-pack/plugins/security_solution/public/plugin_contract.ts +++ b/x-pack/plugins/security_solution/public/plugin_contract.ts @@ -15,16 +15,19 @@ import type { ExperimentalFeatures } from '../common/experimental_features'; import { navLinks$ } from './common/links/nav_links'; import { breadcrumbsNav$ } from './common/breadcrumbs'; import { ContractComponentsService } from './contract_components'; +import { OnboardingPageService } from './app/components/onboarding/onboarding_page_service'; export class PluginContract { public componentsService: ContractComponentsService; public upsellingService: UpsellingService; + public onboardingPageService: OnboardingPageService; public extraRoutes$: BehaviorSubject; public appLinksSwitcher: AppLinksSwitcher; public deepLinksFormatter?: DeepLinksFormatter; constructor(private readonly experimentalFeatures: ExperimentalFeatures) { this.extraRoutes$ = new BehaviorSubject([]); + this.onboardingPageService = new OnboardingPageService(); this.componentsService = new ContractComponentsService(); this.upsellingService = new UpsellingService(); this.appLinksSwitcher = (appLinks) => appLinks; @@ -35,6 +38,7 @@ export class PluginContract { extraRoutes$: this.extraRoutes$.asObservable(), getComponents$: this.componentsService.getComponents$.bind(this.componentsService), upselling: this.upsellingService, + onboarding: this.onboardingPageService, }; } @@ -53,6 +57,7 @@ export class PluginContract { public getStartContract(): PluginStart { return { + setOnboardingPageSettings: this.onboardingPageService, getNavLinks$: () => navLinks$, setExtraRoutes: (extraRoutes) => this.extraRoutes$.next(extraRoutes), setComponents: (components) => { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 16a75ce105262..bcfeba0b6d985 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -83,6 +83,7 @@ import type { ExperimentalFeatures } from '../common/experimental_features'; import type { DeepLinksFormatter } from './common/links/deep_links'; import type { SetComponents, GetComponents$ } from './contract_components'; import type { ConfigSettings } from '../common/config_settings'; +import type { OnboardingPageService } from './app/components/onboarding/onboarding_page_service'; export interface SetupPlugins { cloud?: CloudSetup; @@ -149,6 +150,7 @@ export interface ContractStartServices { extraRoutes$: Observable; getComponents$: GetComponents$; upselling: UpsellingService; + onboarding: OnboardingPageService; } export type StartServices = CoreStart & @@ -188,6 +190,7 @@ export interface PluginStart { setComponents: SetComponents; getBreadcrumbsNav$: () => Observable; getUpselling: () => UpsellingService; + setOnboardingPageSettings: OnboardingPageService; } export interface AppObservableLibs { diff --git a/x-pack/plugins/security_solution_ess/public/get_started/images/cloud1.svg b/x-pack/plugins/security_solution_ess/public/get_started/images/cloud1.svg deleted file mode 100644 index 2dc50321505ae..0000000000000 --- a/x-pack/plugins/security_solution_ess/public/get_started/images/cloud1.svg +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/x-pack/plugins/security_solution_ess/public/get_started/images/endpoint1.svg b/x-pack/plugins/security_solution_ess/public/get_started/images/endpoint1.svg deleted file mode 100644 index bbbb1cd59182c..0000000000000 --- a/x-pack/plugins/security_solution_ess/public/get_started/images/endpoint1.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/x-pack/plugins/security_solution_ess/public/get_started/images/siem1.svg b/x-pack/plugins/security_solution_ess/public/get_started/images/siem1.svg deleted file mode 100644 index 3d1d43bc47ac3..0000000000000 --- a/x-pack/plugins/security_solution_ess/public/get_started/images/siem1.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/x-pack/plugins/security_solution_ess/public/get_started/index.tsx b/x-pack/plugins/security_solution_ess/public/get_started/index.tsx deleted file mode 100644 index 4b512fe2b9884..0000000000000 --- a/x-pack/plugins/security_solution_ess/public/get_started/index.tsx +++ /dev/null @@ -1,13 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import type React from 'react'; - -import { withServicesProvider, type Services } from '../common/services'; -import { GetStarted } from './lazy'; - -export const getSecurityGetStartedComponent = (services: Services): React.ComponentType => - withServicesProvider(GetStarted, services); diff --git a/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.test.tsx b/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.test.tsx deleted file mode 100644 index 153d279a6a06e..0000000000000 --- a/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.test.tsx +++ /dev/null @@ -1,47 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { render } from '@testing-library/react'; -import { LandingCards } from './landing_cards'; -import { ADD_DATA_PATH } from '@kbn/security-solution-plugin/common'; -import { useVariation } from '../common/hooks/use_variation'; - -jest.mock('../common/hooks/use_variation'); -jest.mock('../common/services'); - -describe('LandingCards component', () => { - beforeEach(() => { - (useVariation as jest.Mock).mockReset(); - }); - - it('has add data links', () => { - const { getAllByText } = render(); - expect(getAllByText('Add security integrations')).toHaveLength(2); - }); - - describe.each(['header', 'footer'])('URLs at the %s', (place) => { - it('points to the default Add data URL', () => { - const { queryByTestId } = render(); - const link = queryByTestId(`add-integrations-${place}`); - expect(link?.getAttribute('href')).toBe(ADD_DATA_PATH); - }); - - it('points to the resolved Add data URL by useVariation', () => { - const customResolvedUrl = '/test/url'; - (useVariation as jest.Mock).mockImplementationOnce( - (cloudExperiments, featureFlagName, defaultValue, setter) => { - setter(customResolvedUrl); - } - ); - - const { queryByTestId } = render(); - const link = queryByTestId(`add-integrations-${place}`); - expect(link?.getAttribute('href')).toBe(customResolvedUrl); - }); - }); -}); diff --git a/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.tsx b/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.tsx deleted file mode 100644 index 91e3c2c5f1fc8..0000000000000 --- a/x-pack/plugins/security_solution_ess/public/get_started/landing_cards.tsx +++ /dev/null @@ -1,181 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, useMemo, useState } from 'react'; -import { - EuiButton, - EuiCard, - EuiFlexGroup, - EuiFlexItem, - EuiPageHeader, - useEuiTheme, - type EuiThemeComputed, -} from '@elastic/eui'; -import { css } from '@emotion/react'; -import { ADD_DATA_PATH } from '@kbn/security-solution-plugin/common'; -import { useVariation } from '../common/hooks/use_variation'; -import * as i18n from './translations'; -import endpointSvg from './images/endpoint1.svg'; -import cloudSvg from './images/cloud1.svg'; -import siemSvg from './images/siem1.svg'; -import { useKibana } from '../common/services'; - -const imgUrls = { - cloud: cloudSvg, - siem: siemSvg, - endpoint: endpointSvg, -}; - -const headerCardStyles = css` - span.euiTitle { - font-size: 36px; - line-height: 100%; - } -`; - -const pageHeaderStyles = css` - h1 { - font-size: 18px; - } -`; - -const getFlexItemStyles = (euiTheme: EuiThemeComputed) => css` - background: ${euiTheme.colors.lightestShade}; - padding: 20px; -`; - -const cardStyles = css` - img { - margin-top: 20px; - max-width: 400px; - } -`; - -const footerStyles = css` - span.euiTitle { - font-size: 36px; - line-height: 100%; - } - max-width: 600px; - display: block; - margin: 20px auto 0; -`; - -export const LandingCards = memo(() => { - const { - http: { - basePath: { prepend }, - }, - cloudExperiments, - } = useKibana().services; - - const { euiTheme } = useEuiTheme(); - const [addIntegrationsUrl, setAddIntegrationsUrl] = useState(ADD_DATA_PATH); - useVariation( - cloudExperiments, - 'security-solutions.add-integrations-url', - ADD_DATA_PATH, - setAddIntegrationsUrl - ); - - const href = useMemo(() => prepend(addIntegrationsUrl), [prepend, addIntegrationsUrl]); - - return ( - - - - - - - {i18n.SIEM_CTA} - - } - css={headerCardStyles} - /> - - -