diff --git a/test/functional/services/lib/web_element_wrapper/scroll_into_view_if_necessary.js b/test/functional/services/lib/web_element_wrapper/scroll_into_view_if_necessary.js index 88a6ad0e3ab15..514d1bb1d9d7b 100644 --- a/test/functional/services/lib/web_element_wrapper/scroll_into_view_if_necessary.js +++ b/test/functional/services/lib/web_element_wrapper/scroll_into_view_if_necessary.js @@ -27,7 +27,7 @@ * SOFTWARE. */ -export function scrollIntoViewIfNecessary(target, fixedHeaderHeight) { +export function scrollIntoViewIfNecessary(target, fixedHeaderHeight, fixedFooterHeight) { var rootScroller = document.scrollingElement || document.documentElement; if (!rootScroller) { throw new Error('Unable to find document.scrollingElement or document.documentElement'); @@ -63,4 +63,11 @@ export function scrollIntoViewIfNecessary(target, fixedHeaderHeight) { if (additionalScrollNecessary > 0) { rootScroller.scrollTop = rootScroller.scrollTop - additionalScrollNecessary; } + + if (fixedFooterHeight) { + var bottomOfVisibility = viewportHeight - fixedFooterHeight; + if (bottomOfVisibility < boundingRect.bottom) { + rootScroller.scrollTop = rootScroller.scrollTop + fixedFooterHeight; + } + } } diff --git a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts index 148c21ffac191..4b164402bfb70 100644 --- a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts +++ b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts @@ -692,11 +692,22 @@ export class WebElementWrapper { * @nonstandard * @return {Promise} */ - public async scrollIntoViewIfNecessary(topOffset?: number): Promise { + public async scrollIntoViewIfNecessary( + topOffsetOrOptions?: number | { topOffset?: number; bottomOffset?: number } + ): Promise { + let topOffset: undefined | number; + let bottomOffset: undefined | number; + if (typeof topOffsetOrOptions === 'number') { + topOffset = topOffsetOrOptions; + } else { + topOffset = topOffsetOrOptions?.topOffset; + bottomOffset = topOffsetOrOptions?.bottomOffset; + } await this.driver.executeScript( scrollIntoViewIfNecessary, this._webElement, - topOffset || this.fixedHeaderHeight + topOffset || this.fixedHeaderHeight, + bottomOffset ); } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx index 3e4b120a28f8e..75fc06c1a4494 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx @@ -7,7 +7,7 @@ import type { ReactEventHandler } from 'react'; import React, { useState, useEffect, useMemo, useCallback } from 'react'; -import { useRouteMatch, useHistory } from 'react-router-dom'; +import { useRouteMatch, useHistory, useLocation } from 'react-router-dom'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -49,7 +49,6 @@ import { CreatePackagePolicyPageLayout } from './components'; import type { CreatePackagePolicyFrom, PackagePolicyFormState } from './types'; import type { PackagePolicyValidationResults } from './services'; import { validatePackagePolicy, validationHasErrors } from './services'; -import { StepSelectPackage } from './step_select_package'; import { StepSelectAgentPolicy } from './step_select_agent_policy'; import { StepConfigurePackagePolicy } from './step_configure_package'; import { StepDefinePackagePolicy } from './step_define_package_policy'; @@ -60,9 +59,15 @@ const StepsWithLessPadding = styled(EuiSteps)` } `; +const CustomEuiBottomBar = styled(EuiBottomBar)` + // Set a relatively _low_ z-index value here to account for EuiComboBox popover that might appear under the bottom bar + z-index: 50; +`; + interface AddToPolicyParams { pkgkey: string; integration?: string; + policyId?: string; } interface AddFromPolicyParams { @@ -81,10 +86,14 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { const routeState = useIntraAppState(); const from: CreatePackagePolicyFrom = 'policyId' in params ? 'policy' : 'package'; + const { search } = useLocation(); + const queryParams = useMemo(() => new URLSearchParams(search), [search]); + const policyId = useMemo(() => queryParams.get('policyId') ?? undefined, [queryParams]); + // Agent policy and package info states - const [agentPolicy, setAgentPolicy] = useState(); + const [agentPolicy, setAgentPolicy] = useState(); const [packageInfo, setPackageInfo] = useState(); - const [isLoadingSecondStep, setIsLoadingSecondStep] = useState(false); + const [isLoadingAgentPolicyStep, setIsLoadingAgentPolicyStep] = useState(false); // Retrieve agent count const agentPolicyId = agentPolicy?.id; @@ -286,6 +295,13 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { agentPolicyName: agentPolicy.name, }, }) + : (params as AddToPolicyParams)?.policyId && agentPolicy && agentCount === 0 + ? i18n.translate('xpack.fleet.createPackagePolicy.addAgentNextNotification', { + defaultMessage: `The policy has been updated. Add an agent to the '{agentPolicyName}' policy to deploy this policy.`, + values: { + agentPolicyName: agentPolicy.name, + }, + }) : undefined, 'data-test-subj': 'packagePolicyCreateSuccessToast', }); @@ -337,32 +353,20 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { ), - [params, updatePackageInfo, agentPolicy, updateAgentPolicy] + [params, updatePackageInfo, agentPolicy, updateAgentPolicy, policyId] ); const ExtensionView = useUIExtension(packagePolicy.package?.name ?? '', 'package-policy-create'); - const stepSelectPackage = useMemo( - () => ( - - ), - [params, updateAgentPolicy, packageInfo, updatePackageInfo] - ); - const stepConfigurePackagePolicy = useMemo( () => - isLoadingSecondStep ? ( + isLoadingAgentPolicyStep ? ( ) : agentPolicy && packageInfo ? ( <> @@ -399,7 +403,7 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => {
), [ - isLoadingSecondStep, + isLoadingAgentPolicyStep, agentPolicy, packageInfo, packagePolicy, @@ -413,27 +417,20 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { ); const steps: EuiStepProps[] = [ - from === 'package' - ? { - title: i18n.translate('xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle', { - defaultMessage: 'Select an agent policy', - }), - children: stepSelectAgentPolicy, - } - : { - title: i18n.translate('xpack.fleet.createPackagePolicy.stepSelectPackageTitle', { - defaultMessage: 'Select an integration', - }), - children: stepSelectPackage, - }, { title: i18n.translate('xpack.fleet.createPackagePolicy.stepConfigurePackagePolicyTitle', { defaultMessage: 'Configure integration', }), - status: !packageInfo || !agentPolicy || isLoadingSecondStep ? 'disabled' : undefined, + status: !packageInfo || !agentPolicy || isLoadingAgentPolicyStep ? 'disabled' : undefined, 'data-test-subj': 'dataCollectionSetupStep', children: stepConfigurePackagePolicy, }, + { + title: i18n.translate('xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle', { + defaultMessage: 'Apply to agent policy', + }), + children: stepSelectAgentPolicy, + }, ]; return ( @@ -459,10 +456,10 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { )} - + - {!isLoadingSecondStep && agentPolicy && packageInfo && formState === 'INVALID' ? ( + {!isLoadingAgentPolicyStep && agentPolicy && packageInfo && formState === 'INVALID' ? ( { - + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx index 26d47cbff5b86..e2f5a7249ff0a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx @@ -14,9 +14,11 @@ import { EuiFlexGroup, EuiFlexItem, EuiComboBox, - EuiTextColor, EuiPortal, EuiFormRow, + EuiDescribedFormGroup, + EuiTitle, + EuiText, EuiLink, } from '@elastic/eui'; @@ -32,38 +34,32 @@ import { } from '../../../hooks'; import { CreateAgentPolicyFlyout } from '../list_page/components'; -const AgentPolicyWrapper = styled(EuiFormRow)` +const AgentPolicyFormRow = styled(EuiFormRow)` .euiFormRow__label { width: 100%; } `; -// Custom styling for drop down list items due to: -// 1) the max-width and overflow properties is added to prevent long agent policy -// names/descriptions from overflowing the flex items -// 2) max-width is built from the grow property on the flex items because the value -// changes based on if Fleet is enabled/setup or not -const AgentPolicyNameColumn = styled(EuiFlexItem)` - max-width: ${(props) => `${((props.grow as number) / 9) * 100}%`}; - overflow: hidden; -`; -const AgentPolicyDescriptionColumn = styled(EuiFlexItem)` - max-width: ${(props) => `${((props.grow as number) / 9) * 100}%`}; - overflow: hidden; -`; - export const StepSelectAgentPolicy: React.FunctionComponent<{ pkgkey: string; updatePackageInfo: (packageInfo: PackageInfo | undefined) => void; + defaultAgentPolicyId?: string; agentPolicy: AgentPolicy | undefined; updateAgentPolicy: (agentPolicy: AgentPolicy | undefined) => void; setIsLoadingSecondStep: (isLoading: boolean) => void; -}> = ({ pkgkey, updatePackageInfo, agentPolicy, updateAgentPolicy, setIsLoadingSecondStep }) => { +}> = ({ + pkgkey, + updatePackageInfo, + agentPolicy, + updateAgentPolicy, + setIsLoadingSecondStep, + defaultAgentPolicyId, +}) => { const { isReady: isFleetReady } = useFleetStatus(); // Selected agent policy state const [selectedPolicyId, setSelectedPolicyId] = useState( - agentPolicy ? agentPolicy.id : undefined + agentPolicy?.id ?? defaultAgentPolicyId ); const [selectedAgentPolicyError, setSelectedAgentPolicyError] = useState(); @@ -223,95 +219,91 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ ) : null} - - + +

- - -
- setIsCreateAgentPolicyFlyoutOpen(true)} - > - - -
-
- +

+ } - helpText={ - isFleetReady && selectedPolicyId ? ( - - ) : null + description={ + +

+ +

+
} > - ) => { - return ( - - - {option.label} - - - - {agentPoliciesById[option.value!].description} - - - {isFleetReady ? ( - - - - - - ) : null} - - ); - }} - selectedOptions={selectedAgentPolicyOption ? [selectedAgentPolicyOption] : []} - onChange={(options) => { - const selectedOption = options[0] || undefined; - if (selectedOption) { - if (selectedOption.value !== selectedPolicyId) { - setSelectedPolicyId(selectedOption.value); + label={ + + + + + +
+ setIsCreateAgentPolicyFlyoutOpen(true)} + > + + +
+
+
+ } + helpText={ + isFleetReady && selectedPolicyId ? ( + + ) : null + } + > + -
+ )} + singleSelection={{ asPlainText: true }} + isClearable={false} + fullWidth={true} + isLoading={isAgentPoliciesLoading || isPackageInfoLoading} + options={agentPolicyOptions} + selectedOptions={selectedAgentPolicyOption ? [selectedAgentPolicyOption] : []} + onChange={(options) => { + const selectedOption = options[0] || undefined; + if (selectedOption) { + if (selectedOption.value !== selectedPolicyId) { + setSelectedPolicyId(selectedOption.value); + } + } else { + setSelectedPolicyId(undefined); + } + }} + /> + +
{/* Display selected agent policy error if there is one */} {selectedAgentPolicyError ? ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_package.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_package.tsx deleted file mode 100644 index 50c63274b5e85..0000000000000 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/step_select_package.tsx +++ /dev/null @@ -1,198 +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, { useEffect, useState, Fragment } from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFlexGroup, EuiFlexItem, EuiSelectable, EuiSpacer } from '@elastic/eui'; - -import { Error, PackageIcon } from '../../../components'; -import type { AgentPolicy, PackageInfo, PackagePolicy, GetPackagesResponse } from '../../../types'; -import { - useGetOneAgentPolicy, - useGetPackages, - useGetLimitedPackages, - sendGetPackageInfoByKey, -} from '../../../hooks'; -import { pkgKeyFromPackageInfo } from '../../../services'; - -export const StepSelectPackage: React.FunctionComponent<{ - agentPolicyId: string; - updateAgentPolicy: (agentPolicy: AgentPolicy | undefined) => void; - packageInfo?: PackageInfo; - updatePackageInfo: (packageInfo: PackageInfo | undefined) => void; - setIsLoadingSecondStep: (isLoading: boolean) => void; -}> = ({ - agentPolicyId, - updateAgentPolicy, - packageInfo, - updatePackageInfo, - setIsLoadingSecondStep, -}) => { - // Selected package state - const [selectedPkgKey, setSelectedPkgKey] = useState( - packageInfo ? pkgKeyFromPackageInfo(packageInfo) : undefined - ); - const [selectedPkgError, setSelectedPkgError] = useState(); - - // Fetch agent policy info - const { - data: agentPolicyData, - error: agentPolicyError, - isLoading: isAgentPoliciesLoading, - } = useGetOneAgentPolicy(agentPolicyId); - - // Fetch packages info - // Filter out limited packages already part of selected agent policy - const [packages, setPackages] = useState([]); - const { - data: packagesData, - error: packagesError, - isLoading: isPackagesLoading, - } = useGetPackages(); - const { - data: limitedPackagesData, - isLoading: isLimitedPackagesLoading, - } = useGetLimitedPackages(); - useEffect(() => { - if (packagesData?.response && limitedPackagesData?.response && agentPolicyData?.item) { - const allPackages = packagesData.response; - const limitedPackages = limitedPackagesData.response; - const usedLimitedPackages = (agentPolicyData.item.package_policies as PackagePolicy[]) - .map((packagePolicy) => packagePolicy.package?.name || '') - .filter((pkgName) => limitedPackages.includes(pkgName)); - setPackages(allPackages.filter((pkg) => !usedLimitedPackages.includes(pkg.name))); - } - }, [packagesData, limitedPackagesData, agentPolicyData]); - - // Update parent agent policy state - useEffect(() => { - if (agentPolicyData && agentPolicyData.item) { - updateAgentPolicy(agentPolicyData.item); - } - }, [agentPolicyData, updateAgentPolicy]); - - // Update parent selected package state - useEffect(() => { - const fetchPackageInfo = async () => { - if (selectedPkgKey) { - setIsLoadingSecondStep(true); - const { data, error } = await sendGetPackageInfoByKey(selectedPkgKey); - if (error) { - setSelectedPkgError(error); - updatePackageInfo(undefined); - } else if (data && data.response) { - setSelectedPkgError(undefined); - updatePackageInfo(data.response); - } - setIsLoadingSecondStep(false); - } else { - setSelectedPkgError(undefined); - updatePackageInfo(undefined); - } - }; - if (!packageInfo || selectedPkgKey !== pkgKeyFromPackageInfo(packageInfo)) { - fetchPackageInfo(); - } - }, [selectedPkgKey, packageInfo, updatePackageInfo, setIsLoadingSecondStep]); - - // Display agent policy error if there is one - if (agentPolicyError) { - return ( - - } - error={agentPolicyError} - /> - ); - } - - // Display packages list error if there is one - if (packagesError) { - return ( - - } - error={packagesError} - /> - ); - } - - return ( - - - { - const pkgkey = `${name}-${version}`; - return { - label: title || name, - key: pkgkey, - prepend: , - checked: selectedPkgKey === pkgkey ? 'on' : undefined, - }; - })} - listProps={{ - bordered: true, - }} - searchProps={{ - placeholder: i18n.translate( - 'xpack.fleet.createPackagePolicy.stepSelectPackage.filterPackagesInputPlaceholder', - { - defaultMessage: 'Search for integrations', - } - ), - }} - height={240} - onChange={(options) => { - const selectedOption = options.find((option) => option.checked === 'on'); - if (selectedOption) { - if (selectedOption.key !== selectedPkgKey) { - setSelectedPkgKey(selectedOption.key); - } - } else { - setSelectedPkgKey(undefined); - } - }} - > - {(list, search) => ( - - {search} - - {list} - - )} - - - {/* Display selected package error if there is one */} - {selectedPkgError ? ( - - - } - error={selectedPkgError} - /> - - ) : null} - - ); -}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx index 2e6e7fb984ef0..49af14b7234fa 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx @@ -18,9 +18,11 @@ import { EuiText, } from '@elastic/eui'; +import { INTEGRATIONS_PLUGIN_ID } from '../../../../../../../../common'; +import { pagePathGetters } from '../../../../../../../constants'; import type { AgentPolicy, PackagePolicy } from '../../../../../types'; import { PackageIcon, PackagePolicyActionsMenu } from '../../../../../components'; -import { useCapabilities, useLink } from '../../../../../hooks'; +import { useCapabilities, useStartServices } from '../../../../../hooks'; interface InMemoryPackagePolicy extends PackagePolicy { packageName?: string; @@ -49,7 +51,7 @@ export const PackagePoliciesTable: React.FunctionComponent = ({ agentPolicy, ...rest }) => { - const { getHref } = useLink(); + const { application } = useStartServices(); const hasWriteCapabilities = useCapabilities().write; // With the package policies provided on input, generate the list of package policies @@ -194,8 +196,13 @@ export const PackagePoliciesTable: React.FunctionComponent = ({ { + application.navigateToApp(INTEGRATIONS_PLUGIN_ID, { + path: `#${pagePathGetters.integrations_all()[1]}`, + state: { forAgentPolicyId: agentPolicy.id }, + }); + }} > - - {children} - + + + {children} + + diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/index.ts b/x-pack/plugins/fleet/public/applications/integrations/hooks/index.ts index 7f758e3c472c1..9e4cdde064e70 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/index.ts +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/index.ts @@ -10,3 +10,4 @@ export { useBreadcrumbs } from './use_breadcrumbs'; export { useLinks } from './use_links'; export * from './use_local_search'; export * from './use_package_install'; +export * from './use_agent_policy_context'; diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_agent_policy_context.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_agent_policy_context.tsx new file mode 100644 index 0000000000000..859db79ad159b --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_agent_policy_context.tsx @@ -0,0 +1,36 @@ +/* + * 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 { FunctionComponent } from 'react'; +import React, { createContext, useContext, useRef, useCallback } from 'react'; + +import type { IntegrationsAppBrowseRouteState } from '../../../types'; +import { useIntraAppState } from '../../../hooks'; + +interface AgentPolicyContextValue { + getId(): string | undefined; +} + +const AgentPolicyContext = createContext({ getId: () => undefined }); + +export const AgentPolicyContextProvider: FunctionComponent = ({ children }) => { + const maybeState = useIntraAppState(); + const ref = useRef(maybeState?.forAgentPolicyId); + + const getId = useCallback(() => { + return ref.current; + }, []); + return {children}; +}; + +export const useAgentPolicyContext = () => { + const ctx = useContext(AgentPolicyContext); + if (!ctx) { + throw new Error('useAgentPolicyContext can only be used inside of AgentPolicyContextProvider'); + } + return ctx; +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 68736455b818f..99a29a8194f9b 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -37,7 +37,12 @@ import { INTEGRATIONS_ROUTING_PATHS, pagePathGetters, } from '../../../../constants'; -import { useCapabilities, useGetPackageInfoByKey, useLink } from '../../../../hooks'; +import { + useCapabilities, + useGetPackageInfoByKey, + useLink, + useAgentPolicyContext, +} from '../../../../hooks'; import { pkgKeyFromPackageInfo } from '../../../../services'; import type { CreatePackagePolicyRouteState, @@ -79,6 +84,7 @@ function Breadcrumbs({ packageTitle }: { packageTitle: string }) { } export function Detail() { + const { getId: getAgentPolicyId } = useAgentPolicyContext(); const { pkgkey, panel } = useParams(); const { getHref } = useLink(); const hasWriteCapabilites = useCapabilities().write; @@ -87,6 +93,7 @@ export function Detail() { const queryParams = useMemo(() => new URLSearchParams(search), [search]); const integration = useMemo(() => queryParams.get('integration'), [queryParams]); const services = useStartServices(); + const agentPolicyIdFromContext = getAgentPolicyId(); // Package info state const [packageInfo, setPackageInfo] = useState(null); @@ -211,24 +218,42 @@ export function Detail() { search, hash, }); - const redirectToPath: CreatePackagePolicyRouteState['onSaveNavigateTo'] & - CreatePackagePolicyRouteState['onCancelNavigateTo'] = [ - INTEGRATIONS_PLUGIN_ID, - { - path: currentPath, - }, - ]; - const redirectBackRouteState: CreatePackagePolicyRouteState = { - onSaveNavigateTo: redirectToPath, - onCancelNavigateTo: redirectToPath, - onCancelUrl: currentPath, - }; const path = pagePathGetters.add_integration_to_policy({ pkgkey, ...(integration ? { integration } : {}), + ...(agentPolicyIdFromContext ? { agentPolicyId: agentPolicyIdFromContext } : {}), })[1]; + let redirectToPath: CreatePackagePolicyRouteState['onSaveNavigateTo'] & + CreatePackagePolicyRouteState['onCancelNavigateTo']; + + if (agentPolicyIdFromContext) { + redirectToPath = [ + PLUGIN_ID, + { + path: `#${ + pagePathGetters.policy_details({ + policyId: agentPolicyIdFromContext, + })[1] + }`, + }, + ]; + } else { + redirectToPath = [ + INTEGRATIONS_PLUGIN_ID, + { + path: currentPath, + }, + ]; + } + + const redirectBackRouteState: CreatePackagePolicyRouteState = { + onSaveNavigateTo: redirectToPath, + onCancelNavigateTo: redirectToPath, + onCancelUrl: currentPath, + }; + services.application.navigateToApp(PLUGIN_ID, { // Necessary because of Fleet's HashRouter. Can be changed when // https://github.com/elastic/kibana/issues/96134 is resolved @@ -236,7 +261,16 @@ export function Detail() { state: redirectBackRouteState, }); }, - [history, hash, pathname, search, pkgkey, integration, services.application] + [ + history, + hash, + pathname, + search, + pkgkey, + integration, + services.application, + agentPolicyIdFromContext, + ] ); const headerRightContent = useMemo( @@ -284,6 +318,9 @@ export function Detail() { href={getHref('add_integration_to_policy', { pkgkey, ...(integration ? { integration } : {}), + ...(agentPolicyIdFromContext + ? { agentPolicyId: agentPolicyIdFromContext } + : {}), })} onClick={handleAddIntegrationPolicyClick} data-test-subj="addIntegrationPolicyButton" @@ -325,6 +362,7 @@ export function Detail() { packageInstallStatus, pkgkey, updateAvailable, + agentPolicyIdFromContext, ] ); diff --git a/x-pack/plugins/fleet/public/constants/page_paths.ts b/x-pack/plugins/fleet/public/constants/page_paths.ts index 4ff71cf162e22..3c9c0e5759615 100644 --- a/x-pack/plugins/fleet/public/constants/page_paths.ts +++ b/x-pack/plugins/fleet/public/constants/page_paths.ts @@ -111,9 +111,10 @@ export const pagePathGetters: { FLEET_BASE_PATH, `/policies/${policyId}/add-integration`, ], - add_integration_to_policy: ({ pkgkey, integration }) => [ + add_integration_to_policy: ({ pkgkey, integration, agentPolicyId }) => [ FLEET_BASE_PATH, - `/integrations/${pkgkey}/add-integration${integration ? `/${integration}` : ''}`, + // prettier-ignore + `/integrations/${pkgkey}/add-integration${integration ? `/${integration}` : ''}${agentPolicyId ? `?policyId=${agentPolicyId}` : ''}`, ], edit_integration: ({ policyId, packagePolicyId }) => [ FLEET_BASE_PATH, diff --git a/x-pack/plugins/fleet/public/types/intra_app_route_state.ts b/x-pack/plugins/fleet/public/types/intra_app_route_state.ts index 74921daa0d2a1..36fd32c2a6584 100644 --- a/x-pack/plugins/fleet/public/types/intra_app_route_state.ts +++ b/x-pack/plugins/fleet/public/types/intra_app_route_state.ts @@ -39,6 +39,11 @@ export interface AgentDetailsReassignPolicyAction { onDoneNavigateTo?: Parameters; } +export interface IntegrationsAppBrowseRouteState { + /** The agent policy that we are browsing integrations for */ + forAgentPolicyId: string; +} + /** * All possible Route states. */ diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e47dfdf986eee..0d5d26ad92619 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8996,11 +8996,6 @@ "xpack.fleet.createPackagePolicy.stepConfigure.toggleAdvancedOptionsButtonText": "高度なオプション", "xpack.fleet.createPackagePolicy.stepConfigurePackagePolicyTitle": "統合の構成", "xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle": "エージェントポリシーを選択", - "xpack.fleet.createPackagePolicy.stepSelectPackage.errorLoadingPackagesTitle": "統合の読み込みエラー", - "xpack.fleet.createPackagePolicy.stepSelectPackage.errorLoadingPolicyTitle": "エージェントポリシー情報の読み込みエラー", - "xpack.fleet.createPackagePolicy.stepSelectPackage.errorLoadingSelectedPackageTitle": "選択した統合の読み込みエラー", - "xpack.fleet.createPackagePolicy.stepSelectPackage.filterPackagesInputPlaceholder": "統合を検索", - "xpack.fleet.createPackagePolicy.stepSelectPackageTitle": "統合を選択", "xpack.fleet.createPackagePolicy.StepSelectPolicy.addButton": "エージェントポリシーを作成", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyLabel": "エージェントポリシー", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyPlaceholderText": "この統合を追加するエージェントポリシーを選択", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3ab6203b48942..ca41ef6a22b76 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9077,13 +9077,7 @@ "xpack.fleet.createPackagePolicy.stepConfigure.toggleAdvancedOptionsButtonText": "高级选项", "xpack.fleet.createPackagePolicy.stepConfigurePackagePolicyTitle": "配置集成", "xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle": "选择代理策略", - "xpack.fleet.createPackagePolicy.stepSelectPackage.errorLoadingPackagesTitle": "加载集成时出错", - "xpack.fleet.createPackagePolicy.stepSelectPackage.errorLoadingPolicyTitle": "加载代理策略信息时出错", - "xpack.fleet.createPackagePolicy.stepSelectPackage.errorLoadingSelectedPackageTitle": "加载选定集成时出错", - "xpack.fleet.createPackagePolicy.stepSelectPackage.filterPackagesInputPlaceholder": "搜索集成", - "xpack.fleet.createPackagePolicy.stepSelectPackageTitle": "选择集成", "xpack.fleet.createPackagePolicy.StepSelectPolicy.addButton": "创建代理策略", - "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyAgentsCountText": "{count, plural, other {# 个代理}}已注册", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyAgentsDescriptionText": "{count, plural, other {# 个代理}}已注册到选定代理策略。", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyLabel": "代理策略", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyPlaceholderText": "选择要将此集成添加到的代理策略", diff --git a/x-pack/test/functional/page_objects/synthetics_integration_page.ts b/x-pack/test/functional/page_objects/synthetics_integration_page.ts index 69ae3f43d26f2..56a67d9e6fbd4 100644 --- a/x-pack/test/functional/page_objects/synthetics_integration_page.ts +++ b/x-pack/test/functional/page_objects/synthetics_integration_page.ts @@ -16,6 +16,8 @@ export function SyntheticsIntegrationPageProvider({ const testSubjects = getService('testSubjects'); const comboBox = getService('comboBox'); + const fixedFooterHeight = 72; // Size of EuiBottomBar more or less + return { /** * Navigates to the Synthetics Integration page @@ -85,6 +87,7 @@ export function SyntheticsIntegrationPageProvider({ */ async fillTextInputByTestSubj(testSubj: string, value: string) { const field = await testSubjects.find(testSubj, 5000); + await field.scrollIntoViewIfNecessary({ bottomOffset: fixedFooterHeight }); await field.click(); await field.clearValue(); await field.type(value); @@ -96,6 +99,7 @@ export function SyntheticsIntegrationPageProvider({ * @params {value} the value of the input */ async fillTextInput(field: WebElementWrapper, value: string) { + await field.scrollIntoViewIfNecessary({ bottomOffset: fixedFooterHeight }); await field.click(); await field.clearValue(); await field.type(value);