diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx index eec7d80b75c4b..76994feb18fea 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx @@ -10,17 +10,19 @@ import { Redirect, useRouteMatch, Switch, Route, useHistory, useLocation } from import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedDate } from '@kbn/i18n/react'; import { + EuiButtonEmpty, + EuiDescriptionList, + EuiDescriptionListDescription, + EuiDescriptionListTitle, EuiFlexGroup, EuiFlexItem, + EuiI18nNumber, EuiIconTip, - EuiTitle, - EuiText, + EuiLink, + EuiPortal, EuiSpacer, - EuiButtonEmpty, - EuiI18nNumber, - EuiDescriptionList, - EuiDescriptionListTitle, - EuiDescriptionListDescription, + EuiText, + EuiTitle, } from '@elastic/eui'; import type { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; import styled from 'styled-components'; @@ -36,7 +38,7 @@ import { useFleetStatus, useIntraAppState, } from '../../../hooks'; -import { Loading, Error } from '../../../components'; +import { Loading, Error, AgentEnrollmentFlyout, AddAgentHelpPopover } from '../../../components'; import { WithHeaderLayout } from '../../../layouts'; import { LinkedAgentCount, AgentPolicyActionMenu } from '../components'; @@ -58,7 +60,16 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { const agentPolicyRequest = useGetOneAgentPolicy(policyId); const agentPolicy = agentPolicyRequest.data ? agentPolicyRequest.data.item : null; const { isLoading, error, sendRequest: refreshAgentPolicy } = agentPolicyRequest; + const queryParams = new URLSearchParams(useLocation().search); + const openEnrollmentFlyoutOpenByDefault = queryParams.get('openEnrollmentFlyout') === 'true'; + const openAddAgentHelpPopoverOpenByDefault = queryParams.get('showAddAgentHelp') === 'true'; const [redirectToAgentPolicyList] = useState(false); + const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState( + openEnrollmentFlyoutOpenByDefault + ); + const [isAddAgentHelpPopoverOpen, setIsAddAgentHelpPopoverOpen] = useState( + openAddAgentHelpPopoverOpenByDefault + ); const agentStatusRequest = useGetAgentStatus(policyId); const { refreshAgentStatus } = agentStatusRequest; const { @@ -66,8 +77,7 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { } = useStartServices(); const routeState = useIntraAppState(); const agentStatus = agentStatusRequest.data?.results; - const queryParams = new URLSearchParams(useLocation().search); - const openEnrollmentFlyoutOpenByDefault = queryParams.get('openEnrollmentFlyout') === 'true'; + const { isReady: isFleetReady } = useFleetStatus(); const headerLeftContent = useMemo( @@ -138,6 +148,25 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { [getHref, isLoading, agentPolicy, policyId] ); + const onCancelEnrollment = useMemo(() => { + if (routeState && routeState.onDoneNavigateTo && isFleetReady) { + const [appId, options] = routeState.onDoneNavigateTo; + return () => navigateToApp(appId, options); + } + + return undefined; + }, [isFleetReady, navigateToApp, routeState]); + + const addAgentLink = ( + { + setIsAddAgentHelpPopoverOpen(false); + setIsEnrollmentFlyoutOpen(true); + }} + > + + + ); const headerRightContent = useMemo( () => agentPolicy ? ( @@ -168,15 +197,25 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { { isDivider: true }, { label: i18n.translate('xpack.fleet.policyDetails.summary.usedBy', { - defaultMessage: 'Used by', + defaultMessage: 'Agents', }), - content: ( - - ), + content: + agentStatus && agentStatus!.total ? ( + + ) : ( + { + setIsAddAgentHelpPopoverOpen(false); + }} + /> + ), }, { isDivider: true }, { @@ -203,16 +242,7 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { onCopySuccess={(newAgentPolicy: AgentPolicy) => { history.push(getPath('policy_details', { policyId: newAgentPolicy.id })); }} - enrollmentFlyoutOpenByDefault={openEnrollmentFlyoutOpenByDefault} - onCancelEnrollment={ - routeState && routeState.onDoneNavigateTo && isFleetReady - ? () => - navigateToApp( - routeState.onDoneNavigateTo![0], - routeState.onDoneNavigateTo![1] - ) - : undefined - } + onCancelEnrollment={onCancelEnrollment} /> ), }, @@ -237,7 +267,7 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { ) : undefined, /* eslint-disable-next-line react-hooks/exhaustive-deps */ - [agentPolicy, policyId, agentStatus] + [agentPolicy, policyId, agentStatus, isAddAgentHelpPopoverOpen] ); const headerTabs = useMemo(() => { @@ -303,8 +333,28 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { ); } - return ; - }, [agentPolicy, policyId, error, isLoading, redirectToAgentPolicyList]); + return ( + <> + {isEnrollmentFlyoutOpen && ( + + setIsEnrollmentFlyoutOpen(false))} + /> + + )} + ; + + ); + }, [ + redirectToAgentPolicyList, + isLoading, + error, + agentPolicy, + isEnrollmentFlyoutOpen, + onCancelEnrollment, + policyId, + ]); return ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx index 8872c61299093..6cb9aab005e77 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx @@ -13,7 +13,12 @@ import { createIntegrationsTestRendererMock } from '../../../../../../../../mock import { PackagePolicyAgentsCell } from './package_policy_agents_cell'; -function renderCell({ agentCount = 0, agentPolicyId = '123', onAddAgent = () => {} }) { +function renderCell({ + agentCount = 0, + agentPolicyId = '123', + onAddAgent = () => {}, + hasHelpPopover = false, +}) { const renderer = createIntegrationsTestRendererMock(); return renderer.render( @@ -21,6 +26,7 @@ function renderCell({ agentCount = 0, agentPolicyId = '123', onAddAgent = () => agentCount={agentCount} agentPolicyId={agentPolicyId} onAddAgent={onAddAgent} + hasHelpPopover={hasHelpPopover} /> ); } @@ -40,4 +46,25 @@ describe('PackagePolicyAgentsCell', () => { expect(utils.queryByText('9999')).toBeInTheDocument(); }); }); + + test('it should display help popover if count is 0 and hasHelpPopover=true', async () => { + const utils = renderCell({ agentCount: 0, hasHelpPopover: true }); + await act(async () => { + expect(utils.queryByText('9999')).not.toBeInTheDocument(); + expect(utils.queryByText('Add agent')).toBeInTheDocument(); + expect( + utils.container.querySelector('[data-test-subj="addAgentHelpPopover"]') + ).toBeInTheDocument(); + }); + }); + test('it should not display help popover if count is > 0 and hasHelpPopover=true', async () => { + const utils = renderCell({ agentCount: 9999, hasHelpPopover: true }); + await act(async () => { + expect(utils.queryByText('9999')).toBeInTheDocument(); + expect(utils.queryByText('Add agent')).not.toBeInTheDocument(); + expect( + utils.container.querySelector('[data-test-subj="addAgentHelpPopover"]') + ).not.toBeInTheDocument(); + }); + }); }); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx index 37543e7e5ae1b..e70d10e735571 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx @@ -5,20 +5,43 @@ * 2.0. */ -import React from 'react'; +import React, { useState } from 'react'; import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { LinkedAgentCount } from '../../../../../../components'; +import { LinkedAgentCount, AddAgentHelpPopover } from '../../../../../../components'; + +const AddAgentButton = ({ onAddAgent }: { onAddAgent: () => void }) => ( + + + +); + +const AddAgentButtonWithPopover = ({ onAddAgent }: { onAddAgent: () => void }) => { + const button = ; + const [isHelpOpen, setIsHelpOpen] = useState(true); + return ( + setIsHelpOpen(false)} + /> + ); +}; export const PackagePolicyAgentsCell = ({ agentPolicyId, agentCount = 0, onAddAgent, + hasHelpPopover = false, }: { agentPolicyId: string; agentCount?: number; + hasHelpPopover?: boolean; onAddAgent: () => void; }) => { if (agentCount > 0) { @@ -31,12 +54,9 @@ export const PackagePolicyAgentsCell = ({ ); } - return ( - - - - ); + if (!hasHelpPopover) { + return ; + } + + return ; }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index 304bdd621b1b2..2a7cab929211a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -81,6 +81,10 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps () => queryParams.get('addAgentToPolicyId'), [queryParams] ); + const showAddAgentHelpForPolicyId = useMemo( + () => queryParams.get('showAddAgentHelpForPolicyId'), + [queryParams] + ); const [flyoutOpenForPolicyId, setFlyoutOpenForPolicyId] = useState( agentPolicyIdFromParams ); @@ -294,6 +298,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps agentPolicyId={agentPolicy.id} agentCount={agentPolicy.agents} onAddAgent={() => setFlyoutOpenForPolicyId(agentPolicy.id)} + hasHelpPopover={showAddAgentHelpForPolicyId === agentPolicy.id} /> ); }, @@ -321,7 +326,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps }, }, ], - [getHref, viewDataStep] + [getHref, showAddAgentHelpForPolicyId, viewDataStep] ); const noItemsMessage = useMemo(() => { diff --git a/x-pack/plugins/fleet/public/components/add_agent_help_popover.tsx b/x-pack/plugins/fleet/public/components/add_agent_help_popover.tsx new file mode 100644 index 0000000000000..e47070fdc7b99 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/add_agent_help_popover.tsx @@ -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 type { ReactElement } from 'react'; +import React from 'react'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +import type { NoArgCallback } from '@elastic/eui'; +import { EuiTourStep, EuiLink, EuiText } from '@elastic/eui'; + +import { useStartServices } from '../hooks'; + +export const AddAgentHelpPopover = ({ + button, + isOpen, + offset, + closePopover, +}: { + button: ReactElement; + isOpen: boolean; + offset?: number; + closePopover: NoArgCallback; +}) => { + const { docLinks } = useStartServices(); + + const optionalProps: { offset?: number } = {}; + + if (offset !== undefined) { + optionalProps.offset = offset; // offset being present in props sets it to 0 so only add if specified + } + + return ( + + Elastic Agent, + learnMoreLink: ( + + + + ), + }} + /> + + } + isStepOpen={isOpen} + minWidth={300} + onFinish={() => {}} + step={1} + stepsTotal={1} + title={ + + } + anchorPosition="downCenter" + subtitle={null} + data-test-subj="addAgentHelpPopover" + footerAction={ + { + closePopover(); + }} + > + + + } + > + {button} + + ); +}; diff --git a/x-pack/plugins/fleet/public/components/index.ts b/x-pack/plugins/fleet/public/components/index.ts index dca0a92076180..3252315312d02 100644 --- a/x-pack/plugins/fleet/public/components/index.ts +++ b/x-pack/plugins/fleet/public/components/index.ts @@ -19,6 +19,7 @@ export { AgentPolicyPackageBadges } from './agent_policy_package_badges'; export { DangerEuiContextMenuItem } from './danger_eui_context_menu_item'; export { PackagePolicyDeleteProvider } from './package_policy_delete_provider'; export { PackagePolicyActionsMenu } from './package_policy_actions_menu'; +export { AddAgentHelpPopover } from './add_agent_help_popover'; export * from './link_and_revision'; export * from './settings_flyout'; export * from './agent_enrollment_flyout';