diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/components/back_link.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/components/back_link.test.tsx
index 613970fc24085..ce8f4e5fd5355 100644
--- a/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/components/back_link.test.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/components/back_link.test.tsx
@@ -5,60 +5,68 @@
* 2.0.
*/
-import { render } from '@testing-library/react';
+import { act, fireEvent, render, waitFor } from '@testing-library/react';
import React from 'react';
import { I18nProvider } from '@kbn/i18n-react';
+import { useStartServices } from '../../../../../../../hooks';
+
import { BackLink } from './back_link';
+jest.mock('../../../../../../../hooks', () => {
+ return {
+ ...jest.requireActual('../../../../../../../hooks'),
+ useStartServices: jest.fn().mockReturnValue({
+ application: { navigateToApp: jest.fn() },
+ }),
+ };
+});
+
describe('BackLink', () => {
- it('renders back to selection link', () => {
- const expectedUrl = '/app/experimental-onboarding';
- const queryParams = new URLSearchParams();
- queryParams.set('observabilityOnboardingLink', expectedUrl);
- const { getByText, getByRole } = render(
-
-
-
- );
- expect(getByText('Back to selection')).toBeInTheDocument();
- expect(getByRole('link').getAttribute('href')).toBe(expectedUrl);
+ beforeEach(() => {
+ jest.mocked(useStartServices().application.navigateToApp).mockReset();
});
- it('renders back to selection link when onboardingLink param is provided', () => {
- const expectedUrl = '/app/experimental-onboarding';
+ it('renders back to selection link when returnAppId and returnPath are present', async () => {
+ const appId = 'observabilityOnboarding';
+ const path = '?category=aws';
const queryParams = new URLSearchParams();
- queryParams.set('onboardingLink', expectedUrl);
- const { getByText, getByRole } = render(
-
-
-
- );
- expect(getByText('Back to selection')).toBeInTheDocument();
- expect(getByRole('link').getAttribute('href')).toBe(expectedUrl);
- });
+ queryParams.set('returnAppId', appId);
+ queryParams.set('returnPath', path);
- it('renders back to selection link with params', () => {
- const expectedUrl = '/app/experimental-onboarding&search=aws&category=infra';
- const queryParams = new URLSearchParams();
- queryParams.set('observabilityOnboardingLink', expectedUrl);
- const { getByText, getByRole } = render(
+ const { getByText } = render(
-
+
);
expect(getByText('Back to selection')).toBeInTheDocument();
- expect(getByRole('link').getAttribute('href')).toBe(expectedUrl);
+ await act(async () => {
+ fireEvent.click(getByText('Back to selection'));
+ });
+ await waitFor(() => {
+ expect(useStartServices().application.navigateToApp).toHaveBeenCalledWith(appId, {
+ path,
+ });
+ });
});
- it('renders back to integrations link', () => {
+ it('renders back to integrations link when no query params are present', async () => {
+ const appId = 'integrations';
+ const path = '/browse';
const queryParams = new URLSearchParams();
- const { getByText, getByRole } = render(
+ const { getByText } = render(
-
+
);
expect(getByText('Back to integrations')).toBeInTheDocument();
- expect(getByRole('link').getAttribute('href')).toBe('/app/integrations');
+ await act(async () => {
+ fireEvent.click(getByText('Back to integrations'));
+ });
+ await waitFor(() => {
+ expect(useStartServices().application.navigateToApp).toHaveBeenCalledWith(appId, {
+ path,
+ });
+ });
});
});
diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/components/back_link.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/components/back_link.tsx
index 75d3461bdfee6..841f57eeb136e 100644
--- a/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/components/back_link.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/components/back_link.tsx
@@ -6,29 +6,47 @@
*/
import { EuiButtonEmpty } from '@elastic/eui';
+
import { FormattedMessage } from '@kbn/i18n-react';
import React, { useMemo } from 'react';
+import { useStartServices } from '../../../../../../../hooks';
+
interface Props {
queryParams: URLSearchParams;
- href: string;
+ integrationsPath: string;
}
-export function BackLink({ queryParams, href: integrationsHref }: Props) {
- const { onboardingLink } = useMemo(() => {
+export function BackLink({ queryParams, integrationsPath }: Props) {
+ const {
+ application: { navigateToApp },
+ } = useStartServices();
+ const { returnAppId, returnPath } = useMemo(() => {
return {
- onboardingLink:
- // Users from Security Solution onboarding page will have onboardingLink to redirect back to the onboarding page
- queryParams.get('observabilityOnboardingLink') || queryParams.get('onboardingLink'),
+ // Check for custom path params to redirect back to a specified app's path
+ returnAppId: queryParams.get('returnAppId'),
+ returnPath: queryParams.get('returnPath'),
};
}, [queryParams]);
- const href = onboardingLink ?? integrationsHref;
- const message = onboardingLink ? BACK_TO_SELECTION : BACK_TO_INTEGRATIONS;
+
+ const appId = returnAppId && returnPath ? returnAppId : 'integrations';
+ const path = returnAppId && returnPath ? returnPath : integrationsPath;
+
+ const message = returnPath ? BACK_TO_SELECTION : BACK_TO_INTEGRATIONS;
return (
-
- {message}
-
+ <>
+ {
+ navigateToApp(appId, { path });
+ }}
+ >
+ {message}
+
+ >
);
}
diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx
index 428ab28c8db16..ed3bc78b5d5ac 100644
--- a/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx
@@ -141,11 +141,11 @@ export function Detail() {
const queryParams = useMemo(() => new URLSearchParams(search), [search]);
const integration = useMemo(() => queryParams.get('integration'), [queryParams]);
const prerelease = useMemo(() => Boolean(queryParams.get('prerelease')), [queryParams]);
- /** Users from Security Solution onboarding page will have onboardingLink and onboardingAppId in the query params
- ** to redirect back to the onboarding page after adding an integration
+ /** Users from Security and Observability Solution onboarding pages will have returnAppId and returnPath
+ ** in the query params to redirect back to the onboarding page after adding an integration
*/
- const onboardingLink = useMemo(() => queryParams.get('onboardingLink'), [queryParams]);
- const onboardingAppId = useMemo(() => queryParams.get('onboardingAppId'), [queryParams]);
+ const returnAppId = useMemo(() => queryParams.get('returnAppId'), [queryParams]);
+ const returnPath = useMemo(() => queryParams.get('returnPath'), [queryParams]);
const authz = useAuthz();
const canAddAgent = authz.fleet.addAgents;
@@ -318,12 +318,12 @@ export function Detail() {
const fromIntegrations = getFromIntegrations();
- const href =
+ const fromIntegrationsPath =
fromIntegrations === 'updates_available'
- ? getHref('integrations_installed_updates_available')
+ ? getPath('integrations_installed_updates_available')
: fromIntegrations === 'installed'
- ? getHref('integrations_installed')
- : getHref('integrations_all');
+ ? getPath('integrations_installed')
+ : getPath('integrations_all');
const numOfDeferredInstallations = useMemo(
() => getDeferredInstallationsCnt(packageInfo),
@@ -336,7 +336,7 @@ export function Detail() {
{/* Allows button to break out of full width */}
-
+
@@ -393,7 +393,7 @@ export function Detail() {
),
- [integrationInfo, isLoading, packageInfo, href, queryParams]
+ [integrationInfo, isLoading, packageInfo, fromIntegrationsPath, queryParams]
);
const handleAddIntegrationPolicyClick = useCallback(
@@ -418,20 +418,20 @@ export function Detail() {
isAgentlessIntegration: isAgentlessIntegration(packageInfo || undefined),
});
- /** Users from Security Solution onboarding page will have onboardingLink and onboardingAppId in the query params
- ** to redirect back to the onboarding page after adding an integration
+ /** Users from Security and Observability Solution onboarding pages will have returnAppId and returnPath
+ ** in the query params to redirect back to the onboarding page after adding an integration
*/
const navigateOptions: InstallPkgRouteOptions =
- onboardingAppId && onboardingLink
+ returnAppId && returnPath
? [
defaultNavigateOptions[0],
{
...defaultNavigateOptions[1],
state: {
...(defaultNavigateOptions[1]?.state ?? {}),
- onCancelNavigateTo: [onboardingAppId, { path: onboardingLink }],
- onCancelUrl: onboardingLink,
- onSaveNavigateTo: [onboardingAppId, { path: onboardingLink }],
+ onCancelNavigateTo: [returnAppId, { path: returnPath }],
+ onCancelUrl: services.application.getUrlForApp(returnAppId, { path: returnPath }),
+ onSaveNavigateTo: [returnAppId, { path: returnPath }],
},
},
]
@@ -448,8 +448,8 @@ export function Detail() {
isCloud,
isFirstTimeAgentUser,
isGuidedOnboardingActive,
- onboardingAppId,
- onboardingLink,
+ returnAppId,
+ returnPath,
packageInfo,
pathname,
pkgkey,
diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.test.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.test.ts
index b0746f798f309..f7b8725c40744 100644
--- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.test.ts
+++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.test.ts
@@ -5,60 +5,37 @@
* 2.0.
*/
-import { addPathParamToUrl, toOnboardingPath } from './use_card_url_rewrite';
+import { addPathParamToUrl, buildOnboardingPath } from './use_card_url_rewrite';
describe('useIntegratrionCardList', () => {
describe('toOnboardingPath', () => {
- it('returns null if no `basePath` is defined', () => {
- expect(toOnboardingPath({})).toBeNull();
- });
- it('returns just the `basePath` if no category or search is defined', () => {
- expect(toOnboardingPath({ basePath: '' })).toBe('/app/observabilityOnboarding');
- expect(toOnboardingPath({ basePath: '/s/custom_space_name' })).toBe(
- '/s/custom_space_name/app/observabilityOnboarding'
- );
- });
it('includes category in the URL', () => {
- expect(toOnboardingPath({ basePath: '/s/custom_space_name', category: 'logs' })).toBe(
- '/s/custom_space_name/app/observabilityOnboarding?category=logs'
- );
- expect(toOnboardingPath({ basePath: '', category: 'infra' })).toBe(
- '/app/observabilityOnboarding?category=infra'
- );
+ expect(buildOnboardingPath({ category: 'logs' })).toBe('?category=logs');
});
it('includes search in the URL', () => {
- expect(toOnboardingPath({ basePath: '/s/custom_space_name', search: 'search' })).toBe(
- '/s/custom_space_name/app/observabilityOnboarding?search=search'
- );
+ expect(buildOnboardingPath({ search: 'search' })).toBe('?search=search');
});
it('includes category and search in the URL', () => {
expect(
- toOnboardingPath({ basePath: '/s/custom_space_name', category: 'logs', search: 'search' })
- ).toBe('/s/custom_space_name/app/observabilityOnboarding?category=logs&search=search');
- expect(toOnboardingPath({ basePath: '', category: 'infra', search: 'search' })).toBe(
- '/app/observabilityOnboarding?category=infra&search=search'
- );
+ buildOnboardingPath({
+ category: 'logs',
+ search: 'search',
+ })
+ ).toBe('?category=logs&search=search');
});
});
+
describe('addPathParamToUrl', () => {
it('adds the onboarding link to url with existing params', () => {
expect(
- addPathParamToUrl(
- '/app/integrations?query-1',
- '/app/observabilityOnboarding?search=aws&category=infra'
- )
+ addPathParamToUrl('/app/integrations?query-1', { search: 'aws', category: 'infra' })
).toBe(
- '/app/integrations?query-1&observabilityOnboardingLink=%2Fapp%2FobservabilityOnboarding%3Fsearch%3Daws%26category%3Dinfra'
+ '/app/integrations?query-1&returnAppId=observabilityOnboarding&returnPath=%3Fcategory%3Dinfra%26search%3Daws'
);
});
it('adds the onboarding link to url without existing params', () => {
- expect(
- addPathParamToUrl(
- '/app/integrations',
- '/app/experimental-onboarding?search=aws&category=infra'
- )
- ).toBe(
- '/app/integrations?observabilityOnboardingLink=%2Fapp%2Fexperimental-onboarding%3Fsearch%3Daws%26category%3Dinfra'
+ expect(addPathParamToUrl('/app/integrations', {})).toBe(
+ '/app/integrations?returnAppId=observabilityOnboarding&returnPath=%3F'
);
});
});
diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.ts
index ce462566bc4d1..63faad1253a84 100644
--- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.ts
+++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.ts
@@ -5,45 +5,50 @@
* 2.0.
*/
-import { useMemo } from 'react';
import { IntegrationCardItem } from '@kbn/fleet-plugin/public';
-import { useKibana } from '@kbn/kibana-react-plugin/public';
+import { OBSERVABILITY_ONBOARDING_APP_ID } from '@kbn/deeplinks-observability';
-export function toOnboardingPath({
- basePath,
+export function buildOnboardingPath({
category,
search,
}: {
- basePath?: string;
category?: string | null;
search?: string;
-}): string | null {
- if (typeof basePath !== 'string' && !basePath) return null;
- const path = `${basePath}/app/observabilityOnboarding`;
- if (!category && !search) return path;
+}): string {
+ if (!category && !search) return '?';
const params = new URLSearchParams();
if (category) params.append('category', category);
if (search) params.append('search', search);
- return `${path}?${params.toString()}`;
+ return `?${params.toString()}`;
}
-export function addPathParamToUrl(url: string, onboardingLink: string) {
- const encoded = encodeURIComponent(onboardingLink);
+export function addPathParamToUrl(
+ url: string,
+ params: {
+ category?: string | null;
+ search?: string;
+ }
+) {
+ const onboardingPath = buildOnboardingPath(params);
+ const encoded = encodeURIComponent(onboardingPath);
+ const paramsString = `returnAppId=${OBSERVABILITY_ONBOARDING_APP_ID}&returnPath=${encoded}`;
+
if (url.indexOf('?') >= 0) {
- return `${url}&observabilityOnboardingLink=${encoded}`;
+ return `${url}&${paramsString}`;
}
- return `${url}?observabilityOnboardingLink=${encoded}`;
+ return `${url}?${paramsString}`;
}
export function useCardUrlRewrite(props: { category?: string | null; search?: string }) {
- const kibana = useKibana();
- const basePath = kibana.services.http?.basePath.get();
- const onboardingLink = useMemo(() => toOnboardingPath({ basePath, ...props }), [basePath, props]);
+ const params = new URLSearchParams();
+ if (props.category) params.append('category', props.category);
+ if (props.search) params.append('search', props.search);
+
return (card: IntegrationCardItem) => ({
...card,
url:
- card.url.indexOf('/app/integrations') >= 0 && onboardingLink
- ? addPathParamToUrl(card.url, onboardingLink)
+ card.url.indexOf('/app/integrations') >= 0
+ ? addPathParamToUrl(card.url, { category: props.category, search: props.search })
: card.url,
});
}
diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/constants.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/constants.ts
index c748f5205e7aa..f8d60a11481ca 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/constants.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/constants.ts
@@ -18,8 +18,8 @@ export const FLEET_APP_ID = `fleet`;
export const INTEGRATION_APP_ID = `integrations`;
export const LOADING_SKELETON_TEXT_LINES = 10; // 10 lines of text
export const MAX_CARD_HEIGHT_IN_PX = 127; // px
-export const ONBOARDING_APP_ID = 'onboardingAppId';
-export const ONBOARDING_LINK = 'onboardingLink';
+export const RETURN_APP_ID = 'returnAppId';
+export const RETURN_PATH = 'returnPath';
export const SCROLL_ELEMENT_ID = 'integrations-scroll-container';
export const SEARCH_FILTER_CATEGORIES: CategoryFacet[] = [];
export const WITH_SEARCH_BOX_HEIGHT = '568px';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts
index 660464ba73501..4a4eb99689c3d 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts
@@ -18,8 +18,8 @@ import {
CARD_TITLE_LINE_CLAMP,
INTEGRATION_APP_ID,
MAX_CARD_HEIGHT_IN_PX,
- ONBOARDING_APP_ID,
- ONBOARDING_LINK,
+ RETURN_APP_ID,
+ RETURN_PATH,
TELEMETRY_INTEGRATION_CARD,
} from './constants';
import type { GetAppUrl, NavigateTo } from '../../../../../common/lib/kibana';
@@ -27,7 +27,7 @@ import { trackOnboardingLinkClick } from '../../../lib/telemetry';
const addPathParamToUrl = (url: string, onboardingLink: string) => {
const encoded = encodeURIComponent(onboardingLink);
- const paramsString = `${ONBOARDING_LINK}=${encoded}&${ONBOARDING_APP_ID}=${APP_UI_ID}`;
+ const paramsString = `${RETURN_PATH}=${encoded}&${RETURN_APP_ID}=${APP_UI_ID}`;
if (url.indexOf('?') >= 0) {
return `${url}&${paramsString}`;
@@ -89,7 +89,7 @@ const addSecuritySpecificProps = ({
};
const url =
card.url.indexOf(APP_INTEGRATIONS_PATH) >= 0 && onboardingLink
- ? addPathParamToUrl(card.url, onboardingLink)
+ ? addPathParamToUrl(card.url, ONBOARDING_PATH)
: card.url;
return {
...card,