From e84d0a0e0c5ee62b0d38f7c1268992163393ecb8 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Tue, 5 Dec 2023 09:50:50 +0000 Subject: [PATCH] [SecuritySolution] Update get started page UI (#171078) ## Summary [Test env](https://p.elstc.co/paste/Y83spa-G#0BRN87aMZxLJfbVok3W0U-7D/sss9OGfH9IIqw4oHL6) 1. When landing on the page the first time, all the tasks are collapsed. If visited before, it keeps the last expanded task. 2. When clicking on the task, the url has the task id appended as `#{taskId}` 3. When visiting the page, if url has `#{taskId}`, the relevant task should be expanded. If no `#{taskId}` in the url, it expands the last expanded task according to **local storage**. 4. Tasks completion are checked automatically, users are not able to undo any tasks unless the **local storage** is cleanned. 5. Task completion criteria: https://github.com/elastic/security-team/issues/8032 `Onboarding tasks with success criteria`: - [x] "create first project" -> ~unexpandable~, already complete when user arrives - [x] "watch overview video" -> user clicks to expand (success == users opens section/clicks "Start") - [x] "add integrations" -> users clicks to expand and goes to integration page (success == query fleet to confirm an agent exists with an integration) `Update we use **indicesExist** from sourcerer to replace fleet api as some performance issue found when running locally, not sure how it will affect the production.` - [x] - after user clicks "Start" and expands, if success criteria is already met (e.g. agent is installed with an integration), notify user agent is installed and mark step as complete. - [x] "view and analyze dashboards" -> users clicks to expand (success == click action) - [x] "enable prebuilt rules" -> clicks to expand (success == at least one rule **enabled**, show enabled rules like integrations above) - [x] "view alerts" -> user clicks to expand (success == click action) 6. Design: https://github.com/elastic/kibana/pull/171078#issuecomment-1828562066 https://github.com/elastic/kibana/issues/170643 Screenshot 2023-12-04 at 16 47 48 Screenshot 2023-12-04 at 16 29 50 Screenshot 2023-12-04 at 16 30 47 - Integration added: https://github.com/elastic/kibana/assets/6295984/1f9aefe4-c20b-4d46-b8b0-1aabf8bd7091 - Integration not added: https://github.com/elastic/kibana/assets/6295984/8b0d6c6b-0bae-4857-aeb1-715f9f4080b8 https://github.com/elastic/kibana/assets/6295984/29432bfe-f270-4e5e-a1c9-86ad806ea5bb ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Sergi Massaneda --- packages/kbn-optimizer/limits.yml | 2 +- .../plugins/security_solution/common/index.ts | 2 + .../components/landing_page/index.test.tsx | 3 + .../common/components/landing_page/index.tsx | 4 +- .../public/contract_components.ts | 2 +- .../plugins/security_solution/public/index.ts | 1 + .../hooks/__mocks__/use_user_name.ts} | 7 +- .../public/common/hooks/use_user_name.ts | 27 + .../public/get_started/apis/index.ts | 32 + .../public/get_started/card_item.test.tsx | 119 +-- .../public/get_started/card_item.tsx | 160 ++-- .../get_started/card_step/index.test.tsx | 142 ++- .../public/get_started/card_step/index.tsx | 245 +++-- .../card_step/overview_video_description.tsx | 18 + .../card_step/step_content.test.tsx | 51 +- .../get_started/card_step/step_content.tsx | 161 ++-- .../public/get_started/card_step/video.tsx | 86 ++ .../context/__mocks__/step_context.tsx | 27 + .../get_started/context/step_context.tsx | 37 + .../public/get_started/get_started.test.tsx | 41 +- .../public/get_started/get_started.tsx | 100 +- .../public/get_started/helpers.test.ts | 140 +-- .../public/get_started/helpers.ts | 22 +- .../hooks/use_check_step_completed.tsx | 77 ++ .../use_setup_sections.test.tsx} | 29 +- .../use_setup_sections.tsx} | 72 +- .../hooks/use_toggle_panel.test.tsx | 360 ++++++++ .../{ => hooks}/use_toggle_panel.tsx | 112 ++- .../get_started/images/create_projects.png | Bin 0 -> 81777 bytes ...elastic_agent_to_protect_your_endpoint.png | Bin 66701 -> 0 bytes .../public/get_started/images/explore.svg | 12 - .../public/get_started/images/icon_cross.svg | 10 - .../public/get_started/images/icon_step.svg | 3 - .../public/get_started/images/invite.svg | 287 ------ .../public/get_started/images/launch.png | Bin 0 -> 63819 bytes .../images/learn_about_elastic_agent.png | Bin 47791 -> 0 bytes .../get_started/images/overview_video.svg | 10 + .../public/get_started/images/progress.svg | 872 ------------------ .../public/get_started/images/protect.svg | 4 - .../public/get_started/index.tsx | 6 +- .../public/get_started/lazy.tsx | 4 +- .../public/get_started/progress_bar.tsx | 54 ++ .../public/get_started/reducer.test.ts | 167 ++-- .../public/get_started/reducer.tsx | 30 +- .../public/get_started/sections.tsx | 224 +++-- .../step_links/add_elastic_rules_button.tsx | 34 +- .../step_links/add_integration_button.tsx | 4 +- .../step_links/add_integration_callout.tsx | 84 ++ .../get_started/step_links/alerts_link.tsx | 53 +- .../step_links/dashboard_button.tsx | 52 +- .../step_links/fleet_overview_link.tsx | 25 - .../step_links/install_agent_button.tsx | 28 - .../step_links/manage_projects_button.tsx | 32 + .../get_started/step_links/translations.ts | 64 ++ .../public/get_started/storage.test.ts | 250 +++-- .../public/get_started/storage.ts | 83 +- .../styles/add_integrations_callout.styles.ts | 51 + .../get_started/styles/card_item.styles.ts | 41 + .../get_started/styles/card_step.styles.ts | 89 ++ .../get_started/styles/step_content.styles.ts | 79 ++ .../styles/welcome_header.styles.ts | 71 ++ .../public/get_started/toggle_panel.test.tsx | 76 +- .../public/get_started/toggle_panel.tsx | 38 +- .../public/get_started/translations.ts | 307 ++---- .../public/get_started/types.ts | 121 ++- .../get_started/use_toggle_panel.test.tsx | 284 ------ .../get_started/welcome_header/index.tsx | 94 ++ .../product_tier_badge.test.tsx | 0 .../product_tier_badge.tsx | 0 .../welcome_header/translations.ts | 21 + .../welcome_panel/change_plan_link.test.tsx | 51 - .../welcome_panel/change_plan_link.tsx | 63 -- .../get_started/welcome_panel/index.test.tsx | 97 -- .../get_started/welcome_panel/index.tsx | 90 -- .../welcome_panel/progress_tracker.test.tsx | 32 - .../welcome_panel/progress_tracker.tsx | 46 - .../get_started/welcome_panel/translations.ts | 62 -- .../public/get_started/welcome_panel/types.ts | 15 - .../welcome_panel/use_welcome_panel.test.tsx | 34 - .../welcome_panel/use_welcome_panel.tsx | 98 -- .../translations/translations/fr-FR.json | 55 -- .../translations/translations/ja-JP.json | 55 -- .../translations/translations/zh-CN.json | 55 -- 83 files changed, 2841 insertions(+), 3655 deletions(-) rename x-pack/plugins/security_solution_serverless/public/{get_started/welcome_panel/__mocks__/index.tsx => common/hooks/__mocks__/use_user_name.ts} (60%) create mode 100644 x-pack/plugins/security_solution_serverless/public/common/hooks/use_user_name.ts create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/apis/index.ts create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/card_step/overview_video_description.tsx create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/card_step/video.tsx create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/context/__mocks__/step_context.tsx create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/context/step_context.tsx create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_check_step_completed.tsx rename x-pack/plugins/security_solution_serverless/public/get_started/{use_setup_cards.test.tsx => hooks/use_setup_sections.test.tsx} (72%) rename x-pack/plugins/security_solution_serverless/public/get_started/{use_setup_cards.tsx => hooks/use_setup_sections.tsx} (69%) create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/hooks/use_toggle_panel.test.tsx rename x-pack/plugins/security_solution_serverless/public/get_started/{ => hooks}/use_toggle_panel.tsx (56%) create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/create_projects.png delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/deploy_elastic_agent_to_protect_your_endpoint.png delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/explore.svg delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/icon_cross.svg delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/icon_step.svg delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/invite.svg create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/launch.png delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/learn_about_elastic_agent.png create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/overview_video.svg delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/progress.svg delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/images/protect.svg create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/progress_bar.tsx create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/step_links/add_integration_callout.tsx delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/step_links/fleet_overview_link.tsx delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/step_links/install_agent_button.tsx create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/step_links/manage_projects_button.tsx create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/step_links/translations.ts create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/styles/add_integrations_callout.styles.ts create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/styles/card_item.styles.ts create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/styles/card_step.styles.ts create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/styles/step_content.styles.ts create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/styles/welcome_header.styles.ts delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/use_toggle_panel.test.tsx create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/index.tsx rename x-pack/plugins/security_solution_serverless/public/get_started/{welcome_panel => welcome_header}/product_tier_badge.test.tsx (100%) rename x-pack/plugins/security_solution_serverless/public/get_started/{welcome_panel => welcome_header}/product_tier_badge.tsx (100%) create mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_header/translations.ts delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/change_plan_link.test.tsx delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/change_plan_link.tsx delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/index.test.tsx delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/index.tsx delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/progress_tracker.test.tsx delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/progress_tracker.tsx delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/translations.ts delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/types.ts delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/use_welcome_panel.test.tsx delete mode 100644 x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/use_welcome_panel.tsx diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 976b423fc50f1..bdda39ab9a546 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -124,7 +124,7 @@ pageLoadAssetSize: screenshotting: 22870 searchprofiler: 67080 security: 81771 - securitySolution: 67584 + securitySolution: 82780 securitySolutionEss: 16573 securitySolutionServerless: 62488 serverless: 16573 diff --git a/x-pack/plugins/security_solution/common/index.ts b/x-pack/plugins/security_solution/common/index.ts index 9c0fe90ba8572..24181c66f7444 100644 --- a/x-pack/plugins/security_solution/common/index.ts +++ b/x-pack/plugins/security_solution/common/index.ts @@ -17,8 +17,10 @@ export { MANAGE_PATH, ADD_DATA_PATH, SecurityPageName, + DETECTION_ENGINE_RULES_URL_FIND, } from './constants'; export { ELASTIC_SECURITY_RULE_ID } from './detection_engine/constants'; +export { ENABLED_FIELD } from './detection_engine/rule_management/rule_fields'; export { allowedExperimentalValues, type ExperimentalFeatures } from './experimental_features'; // Careful of exporting anything from this file as any file(s) you export here will cause your page bundle size to increase. 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 9d676016be252..359cb39ee001b 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 @@ -12,6 +12,9 @@ const mockUseContractComponents = jest.fn(() => ({})); jest.mock('../../hooks/use_contract_component', () => ({ useContractComponents: () => mockUseContractComponents(), })); +jest.mock('../../containers/sourcerer', () => ({ + useSourcererDataView: jest.fn().mockReturnValue({ indicesExist: false }), +})); describe('LandingPageComponent', () => { beforeEach(() => { 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 959827aec3ba4..d013fdd715918 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 @@ -6,11 +6,13 @@ */ import React, { memo } from 'react'; +import { useSourcererDataView } from '../../containers/sourcerer'; import { useContractComponents } from '../../hooks/use_contract_component'; export const LandingPageComponent = memo(() => { const { GetStarted } = useContractComponents(); - return GetStarted ? : null; + const { indicesExist } = useSourcererDataView(); + return GetStarted ? : null; }); LandingPageComponent.displayName = 'LandingPageComponent'; diff --git a/x-pack/plugins/security_solution/public/contract_components.ts b/x-pack/plugins/security_solution/public/contract_components.ts index 3d1dee67eab94..8f5072f43f033 100644 --- a/x-pack/plugins/security_solution/public/contract_components.ts +++ b/x-pack/plugins/security_solution/public/contract_components.ts @@ -9,7 +9,7 @@ import { BehaviorSubject } from 'rxjs'; import type { Observable } from 'rxjs'; export type ContractComponents = Partial<{ - GetStarted: React.ComponentType<{}>; + GetStarted: React.ComponentType<{ indicesExist?: boolean }>; DashboardsLandingCallout: React.ComponentType<{}>; }>; diff --git a/x-pack/plugins/security_solution/public/index.ts b/x-pack/plugins/security_solution/public/index.ts index 32378330e27dd..3af2b724b014e 100644 --- a/x-pack/plugins/security_solution/public/index.ts +++ b/x-pack/plugins/security_solution/public/index.ts @@ -10,6 +10,7 @@ import { Plugin } from './plugin'; import type { PluginSetup, PluginStart } from './types'; export type { TimelineModel } from './timelines/store/timeline/model'; export type { LinkItem } from './common/links'; +export type { FetchRulesResponse } from './detection_engine/rule_management/logic/types'; export const plugin = (context: PluginInitializerContext): Plugin => new Plugin(context); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/__mocks__/index.tsx b/x-pack/plugins/security_solution_serverless/public/common/hooks/__mocks__/use_user_name.ts similarity index 60% rename from x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/__mocks__/index.tsx rename to x-pack/plugins/security_solution_serverless/public/common/hooks/__mocks__/use_user_name.ts index bf5ea333ad7d0..4d811159dab6b 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/welcome_panel/__mocks__/index.tsx +++ b/x-pack/plugins/security_solution_serverless/public/common/hooks/__mocks__/use_user_name.ts @@ -4,9 +4,4 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import React from 'react'; - -export const WelcomePanel = jest - .fn() - .mockImplementation(({ children }) =>
{children}
); +export const useUserName = jest.fn().mockReturnValue('mocked_user_name'); diff --git a/x-pack/plugins/security_solution_serverless/public/common/hooks/use_user_name.ts b/x-pack/plugins/security_solution_serverless/public/common/hooks/use_user_name.ts new file mode 100644 index 0000000000000..5b9c23896d481 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/common/hooks/use_user_name.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 { useEffect, useState } from 'react'; +import { useKibana } from '../services'; + +export const useUserName = () => { + const [userName, setUserName] = useState(); + const { + services: { + security: { authc }, + }, + } = useKibana(); + useEffect(() => { + const getUser = async () => { + const { username } = await authc.getCurrentUser(); + setUserName(username); + }; + + getUser(); + }); + + return userName; +}; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/apis/index.ts b/x-pack/plugins/security_solution_serverless/public/get_started/apis/index.ts new file mode 100644 index 0000000000000..0b515154f178f --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/apis/index.ts @@ -0,0 +1,32 @@ +/* + * 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 { 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'; + +export const fetchRuleManagementFilters = async ({ + http, + signal, + query, +}: { + http: HttpSetup; + signal?: AbortSignal; + query?: { + page: number; + per_page: number; + sort_field: string; + sort_order: string; + filter: string; + }; +}): Promise => + http.fetch(DETECTION_ENGINE_RULES_URL_FIND, { + method: 'GET', + version: '2023-10-31', + signal, + query, + }); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx index bb6844e75b7d1..91417dc8e3ddf 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.test.tsx @@ -5,140 +5,55 @@ * 2.0. */ import React from 'react'; -import { render, fireEvent } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { CardItem } from './card_item'; -import type { CardId, ExpandedCardSteps, StepId } from './types'; -import { GetSetUpCardId, IntroductionSteps, SectionId } from './types'; -import type { EuiThemeComputed } from '@elastic/eui'; -import { introductionSteps } from './sections'; -import { ProductLine } from '../../common/product'; +import type { ExpandedCardSteps, StepId } from './types'; + +import { QuickStartSectionCardsId, SectionId, OverviewSteps } from './types'; jest.mock('./card_step'); describe('CardItemComponent', () => { - const finishedSteps = {} as Record>; - const onCardStepClicked = jest.fn(); + const finishedSteps = new Set([]) as Set; const onStepClicked = jest.fn(); - const onStepButtonClicked = jest.fn(); + const toggleTaskCompleteStatus = jest.fn(); const expandedCardSteps = { - [GetSetUpCardId.introduction]: { + [QuickStartSectionCardsId.watchTheOverviewVideo]: { isExpanded: false, expandedSteps: [] as StepId[], }, } as ExpandedCardSteps; - const mockEuiTheme = { size: { xxs: '4px' }, base: 16 } as EuiThemeComputed; it('should render card', () => { - const { getByText, queryByText } = render( + const { getByTestId } = render( step.id)} - cardId={GetSetUpCardId.introduction} + activeStepIds={[OverviewSteps.getToKnowElasticSecurity]} + cardId={QuickStartSectionCardsId.watchTheOverviewVideo} expandedCardSteps={expandedCardSteps} - euiTheme={mockEuiTheme} finishedSteps={finishedSteps} - onCardClicked={onCardStepClicked} - onStepButtonClicked={onStepButtonClicked} + toggleTaskCompleteStatus={toggleTaskCompleteStatus} onStepClicked={onStepClicked} - sectionId={SectionId.getSetUp} - shadow="" - stepsLeft={1} - timeInMins={30} + sectionId={SectionId.quickStart} /> ); - const cardTitle = getByText('Introduction'); + const cardTitle = getByTestId(QuickStartSectionCardsId.watchTheOverviewVideo); expect(cardTitle).toBeInTheDocument(); - - const step = getByText('1 step left'); - expect(step).toBeInTheDocument(); - - const step1 = queryByText('Step 1'); - expect(step1).not.toBeInTheDocument(); }); it('should not render card when no active steps', () => { const { queryByText } = render( ); const cardTitle = queryByText('Introduction'); expect(cardTitle).not.toBeInTheDocument(); }); - - it('should not render steps left information when all steps are done', () => { - const mockFinishedSteps = { - [GetSetUpCardId.introduction]: new Set([IntroductionSteps.getToKnowElasticSecurity]), - } as Record>; - - const { getByText, queryByText } = render( - step.id)} - cardId={GetSetUpCardId.introduction} - sectionId={SectionId.getSetUp} - expandedCardSteps={expandedCardSteps} - euiTheme={mockEuiTheme} - shadow="" - stepsLeft={0} - timeInMins={0} - onCardClicked={onCardStepClicked} - onStepClicked={onStepClicked} - onStepButtonClicked={onStepButtonClicked} - finishedSteps={mockFinishedSteps} - /> - ); - - const cardTitle = getByText('Introduction'); - expect(cardTitle).toBeInTheDocument(); - - const step = queryByText('1 step left'); - expect(step).not.toBeInTheDocument(); - - const time = queryByText('• About 30 mins'); - expect(time).not.toBeInTheDocument(); - }); - - it('should toggle step expansion on click', () => { - const testCardTitle = 'Introduction'; - const { getByText } = render( - step.id)} - expandedCardSteps={expandedCardSteps} - cardId={GetSetUpCardId.introduction} - sectionId={SectionId.getSetUp} - euiTheme={mockEuiTheme} - shadow="" - stepsLeft={0} - timeInMins={0} - onCardClicked={onCardStepClicked} - onStepClicked={onStepClicked} - onStepButtonClicked={onStepButtonClicked} - finishedSteps={finishedSteps} - /> - ); - - const stepTitle = getByText(testCardTitle); - fireEvent.click(stepTitle); - - expect(onCardStepClicked).toHaveBeenCalledTimes(1); - expect(onCardStepClicked).toHaveBeenCalledWith({ - cardId: GetSetUpCardId.introduction, - isExpanded: true, - }); - }); }); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx index 23d697cc2748a..535b01523c607 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_item.tsx @@ -5,144 +5,98 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiPanel, EuiText, EuiTitle } from '@elastic/eui'; -import type { EuiThemeComputed } from '@elastic/eui'; -import React, { useCallback, useMemo } from 'react'; -import { css } from '@emotion/react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; +import React, { useMemo, useCallback } from 'react'; +import classnames from 'classnames'; import type { CardId, ExpandedCardSteps, - OnCardClicked, - OnStepButtonClicked, + ToggleTaskCompleteStatus, OnStepClicked, SectionId, StepId, } from './types'; -import * as i18n from './translations'; -import { CardStep } from './card_step'; import { getCard } from './helpers'; -import type { ProductLine } from '../../common/product'; +import { CardStep } from './card_step'; +import { useCardItemStyles } from './styles/card_item.styles'; const CardItemComponent: React.FC<{ - activeProducts: Set; activeStepIds: StepId[] | undefined; cardId: CardId; - euiTheme: EuiThemeComputed; expandedCardSteps: ExpandedCardSteps; - finishedSteps: Record>; - onCardClicked: OnCardClicked; - onStepButtonClicked: OnStepButtonClicked; + finishedSteps: Set; + toggleTaskCompleteStatus: ToggleTaskCompleteStatus; onStepClicked: OnStepClicked; sectionId: SectionId; - shadow?: string; - stepsLeft?: number; - timeInMins?: number; }> = ({ - activeProducts, activeStepIds, cardId, - euiTheme, expandedCardSteps, finishedSteps, - onCardClicked, - onStepButtonClicked, + toggleTaskCompleteStatus, onStepClicked, sectionId, - shadow, - stepsLeft, - timeInMins, }) => { + const isExpandedCard = expandedCardSteps[cardId].isExpanded; + const cardItem = useMemo(() => getCard({ cardId, sectionId }), [cardId, sectionId]); - const expandCard = expandedCardSteps[cardId]?.isExpanded ?? false; const expandedSteps = useMemo( () => new Set(expandedCardSteps[cardId]?.expandedSteps ?? []), [cardId, expandedCardSteps] ); - const toggleCard = useCallback( - (e) => { - e.preventDefault(); - const isExpanded = !expandCard; - onCardClicked({ cardId, isExpanded }); - }, - [cardId, expandCard, onCardClicked] + + const cardClassNames = classnames('card-item', { + 'card-expanded': isExpandedCard, + }); + + const cardItemPanelStyle = useCardItemStyles(); + const getCardStep = useCallback( + (stepId: StepId) => cardItem?.steps?.find((step) => step.id === stepId), + [cardItem?.steps] + ); + const steps = useMemo( + () => + activeStepIds?.reduce((acc, stepId) => { + const step = getCardStep(stepId); + if (step && cardItem) { + acc.push( + + ); + } + return acc; + }, []), + [ + activeStepIds, + cardItem, + expandedSteps, + finishedSteps, + getCardStep, + onStepClicked, + sectionId, + toggleTaskCompleteStatus, + ] ); - const hasActiveSteps = activeStepIds != null && activeStepIds.length > 0; - return cardItem && hasActiveSteps ? ( + + return cardItem && activeStepIds ? ( - - - - {cardItem.icon && ( - - )} - - - -

{cardItem.title}

-
-
- {(timeInMins != null || stepsLeft != null) && ( - - - {stepsLeft != null && stepsLeft > 0 && ( - {i18n.STEPS_LEFT(stepsLeft)} - )} - - - )} -
-
- {expandCard && hasActiveSteps && ( - - {[...activeStepIds].map((stepId) => { - return ( - - ); - })} - - )} + {steps}
) : null; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.test.tsx index a20b2c2c270e3..8678714d67af9 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.test.tsx @@ -8,29 +8,59 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { CardStep } from '.'; import type { StepId } from '../types'; -import { GetSetUpCardId, IntroductionSteps, SectionId } from '../types'; -import { ProductLine } from '../../../common/product'; -describe('CardStepComponent', () => { - const step = { - id: IntroductionSteps.getToKnowElasticSecurity, - }; +import { + EnablePrebuiltRulesSteps, + GetStartedWithAlertsCardsId, + QuickStartSectionCardsId, + SectionId, + OverviewSteps, + CreateProjectSteps, +} from '../types'; +import { ALL_DONE_TEXT } from '../translations'; +import { fetchRuleManagementFilters } from '../apis'; +import { createProjectSteps, enablePrebuildRuleSteps, overviewVideoSteps } from '../sections'; + +jest.mock('./step_content', () => ({ + StepContent: () =>
, +})); + +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('@kbn/security-solution-navigation', () => ({ + useNavigateTo: jest.fn().mockReturnValue({ navigateTo: jest.fn() }), + SecurityPageName: { + landing: 'landing', + }, +})); +describe('CardStepComponent', () => { const onStepClicked = jest.fn(); - const onStepButtonClicked = jest.fn(); - const expandedSteps = new Set([IntroductionSteps.getToKnowElasticSecurity]); + const toggleTaskCompleteStatus = jest.fn(); + const expandedSteps = new Set([]); const props = { - activeProducts: new Set([ProductLine.security]), - cardId: GetSetUpCardId.introduction, + cardId: QuickStartSectionCardsId.watchTheOverviewVideo, expandedSteps, - finishedStepsByCard: new Set(), - onStepButtonClicked, + finishedSteps: new Set(), + isExpandedCard: true, + toggleTaskCompleteStatus, onStepClicked, - sectionId: SectionId.getSetUp, - stepId: step.id, + sectionId: SectionId.quickStart, + step: overviewVideoSteps[0], }; - const testStepTitle = 'Get to know Elastic Security'; + const testStepTitle = 'Watch the overview video'; + + beforeEach(() => { + jest.clearAllMocks(); + }); it('should toggle step expansion on click', () => { const { getByText } = render(); @@ -40,54 +70,64 @@ describe('CardStepComponent', () => { expect(onStepClicked).toHaveBeenCalledTimes(1); expect(onStepClicked).toHaveBeenCalledWith({ - sectionId: SectionId.getSetUp, - stepId: IntroductionSteps.getToKnowElasticSecurity, - cardId: GetSetUpCardId.introduction, - isExpanded: false, + sectionId: SectionId.quickStart, + stepId: OverviewSteps.getToKnowElasticSecurity, + cardId: QuickStartSectionCardsId.watchTheOverviewVideo, + isExpanded: true, }); }); - it('should render step title, badges, and description when expanded', () => { - const { getByText, getByTestId } = render(); + it('should render step content when expanded', () => { + const mockProps = { + ...props, + expandedSteps: new Set([ + QuickStartSectionCardsId.watchTheOverviewVideo, + ]) as unknown as Set, + }; + const { getByTestId } = render(); - const stepTitle = getByText(testStepTitle); - fireEvent.click(stepTitle); + const content = getByTestId('mock-step-content'); - const badge1 = getByText('Analytics'); - const badge2 = getByText('Cloud'); - const badge3 = getByText('EDR'); - expect(badge1).toBeInTheDocument(); - expect(badge2).toBeInTheDocument(); - expect(badge3).toBeInTheDocument(); - - const description1 = getByTestId(`${IntroductionSteps.getToKnowElasticSecurity}-description-0`); - const description2 = getByTestId(`${IntroductionSteps.getToKnowElasticSecurity}-description-1`); - expect(description1).toBeInTheDocument(); - expect(description2).toBeInTheDocument(); + expect(content).toBeInTheDocument(); }); - it('should render expended steps', () => { - const { getByTestId } = render(); + it('should not toggle step expansion on click when there is no content', () => { + const mockProps = { + ...props, + stepId: CreateProjectSteps.createFirstProject, + cardId: QuickStartSectionCardsId.createFirstProject, + finishedSteps: new Set([CreateProjectSteps.createFirstProject]), + step: { ...createProjectSteps[0], description: undefined, splitPanel: undefined }, + }; + const { getByText } = render(); + + const stepTitle = getByText('Create your first project'); + fireEvent.click(stepTitle); - const splitPanel = getByTestId('split-panel'); - expect(splitPanel).toBeInTheDocument(); + expect(onStepClicked).toHaveBeenCalledTimes(0); }); - it('should render collapsed steps', () => { - const { queryByTestId } = render(); + it('should not show the step as completed when it is not', () => { + const { queryByText } = render(); - const splitPanel = queryByTestId('split-panel'); - expect(splitPanel).not.toBeInTheDocument(); + const text = queryByText(ALL_DONE_TEXT); + expect(text).not.toBeInTheDocument(); }); - it('should render check icon when stepId is in finishedStepsByCard', () => { - const finishedStepsByCard = new Set([IntroductionSteps.getToKnowElasticSecurity]); - - const { getByTestId } = render( - - ); - - const checkIcon = getByTestId(`${step.id}-icon`); - expect(checkIcon.getAttribute('data-euiicon-type')).toEqual('checkInCircleFilled'); + it('should show the step as completed when it is done', async () => { + (fetchRuleManagementFilters as jest.Mock).mockResolvedValue({ + total: 1, + }); + const mockProps = { + ...props, + cardId: GetStartedWithAlertsCardsId.enablePrebuiltRules, + finishedSteps: new Set([EnablePrebuiltRulesSteps.enablePrebuiltRules]), + sectionId: SectionId.getStartedWithAlerts, + step: enablePrebuildRuleSteps[0], + }; + const { queryByText } = render(); + + const text = queryByText(ALL_DONE_TEXT); + expect(text).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.tsx index c48ea85d80cc8..740fcd6a87625 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/index.tsx @@ -11,78 +11,103 @@ import { EuiFlexItem, EuiIcon, EuiBadge, - useEuiTheme, - EuiButtonEmpty, - useEuiBackgroundColorCSS, + EuiButtonIcon, } from '@elastic/eui'; -import { css } from '@emotion/react'; -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import classnames from 'classnames'; -import type { CardId, OnStepButtonClicked, OnStepClicked, SectionId, StepId } from '../types'; -import icon_step from '../images/icon_step.svg'; -import icon_cross from '../images/icon_cross.svg'; -import { UNDO_MARK_AS_DONE_TITLE, MARK_AS_DONE_TITLE } from '../translations'; -import { getStepsByActiveProduct } from '../helpers'; -import type { ProductLine } from '../../../common/product'; -import { getProductBadges } from '../badge'; +import { useNavigateTo, SecurityPageName } from '@kbn/security-solution-navigation'; + +import type { + CardId, + OnStepClicked, + ToggleTaskCompleteStatus, + SectionId, + StepId, + Step, +} from '../types'; +import { + ALL_DONE_TEXT, + COLLAPSE_STEP_BUTTON_LABEL, + EXPAND_STEP_BUTTON_LABEL, +} from '../translations'; + import { StepContent } from './step_content'; +import { useCheckStepCompleted } from '../hooks/use_check_step_completed'; +import { useStepContext } from '../context/step_context'; +import { useCardStepStyles } from '../styles/card_step.styles'; const CardStepComponent: React.FC<{ - activeProducts: Set; cardId: CardId; expandedSteps: Set; - finishedStepsByCard: Set; - onStepButtonClicked: OnStepButtonClicked; + finishedSteps: Set; + toggleTaskCompleteStatus: ToggleTaskCompleteStatus; onStepClicked: OnStepClicked; sectionId: SectionId; - stepId: StepId; + step: Step; }> = ({ - activeProducts, cardId, expandedSteps, - finishedStepsByCard = new Set(), - onStepButtonClicked, + finishedSteps = new Set(), + toggleTaskCompleteStatus, onStepClicked, sectionId, - stepId, + step, }) => { - const { euiTheme } = useEuiTheme(); - const backgroundColorStyles = useEuiBackgroundColorCSS(); - const isExpandedStep = expandedSteps.has(stepId); - const steps = useMemo( - () => getStepsByActiveProduct({ activeProducts, cardId, sectionId }), - [activeProducts, cardId, sectionId] - ); - const { title, productLineRequired, description, splitPanel } = - steps?.find((step) => step.id === stepId) ?? {}; + const { navigateTo } = useNavigateTo(); - const badges = useMemo(() => getProductBadges(productLineRequired), [productLineRequired]); + const isExpandedStep = expandedSteps.has(step.id); - const toggleStep = useCallback( - (e) => { - e.preventDefault(); - const newState = !isExpandedStep; - onStepClicked({ stepId, cardId, sectionId, isExpanded: newState }); - }, - [cardId, isExpandedStep, onStepClicked, sectionId, stepId] - ); + const { id: stepId, title, description, splitPanel, icon, autoCheckIfStepCompleted } = step; + const hasStepContent = description != null || splitPanel != null; + const { indicesExist } = useStepContext(); - const isDone = finishedStepsByCard.has(stepId); + useCheckStepCompleted({ + autoCheckIfStepCompleted, + cardId, + indicesExist, + sectionId, + stepId, + stepTitle: title, + toggleTaskCompleteStatus, + }); - const hasStepContent = description != null || splitPanel != null; + const isDone = finishedSteps.has(stepId); - const handleStepButtonClicked = useCallback( + const toggleStep = useCallback( (e) => { e.preventDefault(); - onStepButtonClicked({ stepId, cardId, sectionId, undo: isDone ? true : false }); + + if (hasStepContent) { + // Toggle step and sync the expanded card step to storage & reducer + onStepClicked({ stepId, cardId, sectionId, isExpanded: !isExpandedStep }); + + navigateTo({ + deepLinkId: SecurityPageName.landing, + path: `#${stepId}`, + }); + } }, - [cardId, isDone, onStepButtonClicked, sectionId, stepId] + [hasStepContent, onStepClicked, stepId, cardId, sectionId, isExpandedStep, navigateTo] ); + const { + stepPanelStyles, + stepIconStyles, + stepTitleStyles, + allDoneTextStyles, + toggleButtonStyles, + getStepGroundStyles, + stepItemStyles, + } = useCardStepStyles(); + const stepGroundStyles = getStepGroundStyles({ hasStepContent }); + const panelClassNames = classnames({ 'step-panel-collapsed': !isExpandedStep, - 'step-panel-expanded': isExpandedStep, + }); + + const stepIconClassNames = classnames('step-icon', { + 'step-icon-done': isDone, }); return ( @@ -93,108 +118,54 @@ const CardStepComponent: React.FC<{ borderRadius="none" paddingSize="none" className={panelClassNames} - css={css` - padding: ${euiTheme.size.base}; - margin: 0 ${euiTheme.size.s} 0; - - &.step-panel-collapsed:hover { - ${backgroundColorStyles.primary}; - border-radius: ${euiTheme.border.radius.medium}; - } - `} + id={stepId} + css={stepPanelStyles} > - - - + + + + {icon && } + - - - - {title} - - {badges.map((badge) => ( - - {badge.name} - - ))} - + + + {title} + - +
- {isExpandedStep && ( - - {isDone ? UNDO_MARK_AS_DONE_TITLE : MARK_AS_DONE_TITLE} - + {isDone && ( + + {ALL_DONE_TEXT} + )} - {/* Use button here to avoid styles added by EUI*/} - + iconType={isExpandedStep ? 'arrowUp' : 'arrowDown'} + aria-label={isExpandedStep ? COLLAPSE_STEP_BUTTON_LABEL : EXPAND_STEP_BUTTON_LABEL} + size="xs" + css={toggleButtonStyles} + isDisabled={!hasStepContent} + />
- + {hasStepContent && ( +
+
+ +
+
+ )} ); }; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/overview_video_description.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/overview_video_description.tsx new file mode 100644 index 0000000000000..de8f272a0c25d --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/overview_video_description.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { WATCH_VIDEO_DESCRIPTION1, WATCH_VIDEO_DESCRIPTION2 } from '../translations'; + +const OverviewVideoDescriptionComponent = () => ( + <> + {WATCH_VIDEO_DESCRIPTION1} + {WATCH_VIDEO_DESCRIPTION2} + +); + +export const OverviewVideoDescription = React.memo(OverviewVideoDescriptionComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.test.tsx index cb6a18387a538..cbaf99f808f4f 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.test.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.test.tsx @@ -7,43 +7,38 @@ import React from 'react'; import { render } from '@testing-library/react'; import { StepContent } from './step_content'; +import { QuickStartSectionCardsId, SectionId } from '../types'; +import { overviewVideoSteps } from '../sections'; + +jest.mock('../context/step_context'); +jest.mock('../../common/services'); describe('StepContent', () => { - it('renders nothing when hasStepContent is false', () => { - const { container } = render( - - ); + const toggleTaskCompleteStatus = jest.fn(); - expect(container.firstChild).toBeNull(); - }); + const props = { + cardId: QuickStartSectionCardsId.watchTheOverviewVideo, + indicesExist: false, + sectionId: SectionId.quickStart, + step: overviewVideoSteps[0], + toggleTaskCompleteStatus, + }; it('renders step content when hasStepContent is true and isExpandedStep is true', () => { - const description = ['Description Line 1', 'Description Line 2']; - const splitPanel =
{'Split Panel Content'}
; - const { getByTestId, getByText } = render( - - ); + const mockProps = { ...props, hasStepContent: true, isExpandedStep: true }; + const { getByTestId, getByText } = render(); const splitPanelElement = getByTestId('split-panel'); - expect(getByText('Description Line 1')).toBeInTheDocument(); - expect(getByText('Description Line 2')).toBeInTheDocument(); + expect( + getByText( + '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.' + ) + ).toBeInTheDocument(); + expect( + getByText('To explore the platform’s core features, watch the video:') + ).toBeInTheDocument(); expect(splitPanelElement).toBeInTheDocument(); - expect(splitPanelElement).toHaveTextContent('Split Panel Content'); - }); - - it('renders nothing when hasStepContent is true but isExpandedStep is false', () => { - const { container } = render( - - ); - - expect(container.firstChild).toBeNull(); }); }); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.tsx index 372d773829259..881e559dad117 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/step_content.tsx @@ -5,97 +5,92 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, useEuiTheme, useEuiShadow, EuiText } from '@elastic/eui'; -import { css } from '@emotion/react'; +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import React from 'react'; - -const LEFT_CONTENT_PANEL_WIDTH = 486; -const RIGHT_CONTENT_PANEL_WIDTH = 510; -const RIGHT_CONTENT_HEIGHT = 270; -const RIGHT_CONTENT_WIDTH = 480; +import { useCheckStepCompleted } from '../hooks/use_check_step_completed'; +import { useStepContentStyles } from '../styles/step_content.styles'; +import type { + CardId, + CheckIfStepCompleted, + SectionId, + Step, + ToggleTaskCompleteStatus, +} from '../types'; const StepContentComponent = ({ - description, - hasStepContent, - isExpandedStep, - splitPanel, - stepId, + autoCheckIfStepCompleted, + cardId, + indicesExist, + sectionId, + step, + toggleTaskCompleteStatus, }: { - description?: React.ReactNode[]; - hasStepContent: boolean; - isExpandedStep: boolean; - splitPanel?: React.ReactNode; - stepId: string; + autoCheckIfStepCompleted?: CheckIfStepCompleted; + cardId: CardId; + indicesExist: boolean; + sectionId: SectionId; + step: Step; + toggleTaskCompleteStatus: ToggleTaskCompleteStatus; }) => { - const { euiTheme } = useEuiTheme(); - const shadow = useEuiShadow('s'); + const { id: stepId, splitPanel } = step; + const { + stepContentGroupStyles, + leftContentStyles, + descriptionStyles, + rightPanelStyles, + rightPanelContentStyles, + } = useStepContentStyles(); + + useCheckStepCompleted({ + autoCheckIfStepCompleted, + cardId, + indicesExist, + sectionId, + stepId, + stepTitle: step.title, + toggleTaskCompleteStatus, + }); - return hasStepContent && isExpandedStep ? ( - <> - - {description && ( - - - {description?.map((desc, index) => ( -

- {desc} -

- ))} -
-
- )} - {splitPanel && ( - - {splitPanel && ( + return ( + + {step.description && ( + + + {step.description.map((desc, index) => (
- {splitPanel} + {desc}
- )} -
- )} -
- - ) : null; + ))} + +
+ )} + {splitPanel && ( + + {splitPanel && ( +
+ {splitPanel} +
+ )} +
+ )} +
+ ); }; export const StepContent = React.memo(StepContentComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/video.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/video.tsx new file mode 100644 index 0000000000000..e2e04e2fa63c7 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/video.tsx @@ -0,0 +1,86 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiIcon, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import React, { useCallback, useMemo } from 'react'; +import { useStepContext } from '../context/step_context'; +import { WATCH_VIDEO_BUTTON_TITLE } from '../translations'; +import { OverviewSteps, QuickStartSectionCardsId, SectionId } from '../types'; + +const VideoComponent: React.FC = () => { + const { toggleTaskCompleteStatus, finishedSteps } = useStepContext(); + const ref = React.useRef(null); + const [isVideoPlaying, setIsVideoPlaying] = React.useState(false); + const { euiTheme } = useEuiTheme(); + const cardId = QuickStartSectionCardsId.watchTheOverviewVideo; + const isFinishedStep = useMemo( + () => finishedSteps[cardId]?.has(OverviewSteps.getToKnowElasticSecurity), + [finishedSteps, cardId] + ); + + const onVideoClicked = useCallback(() => { + toggleTaskCompleteStatus({ + stepId: OverviewSteps.getToKnowElasticSecurity, + cardId: QuickStartSectionCardsId.watchTheOverviewVideo, + sectionId: SectionId.quickStart, + undo: false, + }); + setIsVideoPlaying(true); + }, [toggleTaskCompleteStatus]); + + return ( +
+ {!isVideoPlaying && !isFinishedStep && ( + + + + + + )} + {(isVideoPlaying || isFinishedStep) && ( +