diff --git a/test/functional/config.js b/test/functional/config.js index 1c0c519f21e4c..19a628be10f52 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -106,6 +106,12 @@ export default async function ({ readConfigFile }) { observabilityCases: { pathname: '/app/observability/cases', }, + fleet: { + pathname: '/app/fleet', + }, + integrations: { + pathname: '/app/integrations', + }, }, junit: { reportName: 'Chrome UI Functional Tests', diff --git a/x-pack/plugins/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx index 5ac594842d392..11be87f146851 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx @@ -10,7 +10,6 @@ import React, { memo, useEffect, useState } from 'react'; import type { AppMountParameters } from 'kibana/public'; import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel, EuiPortal } from '@elastic/eui'; import type { History } from 'history'; -import { createHashHistory } from 'history'; import { Router, Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -20,7 +19,10 @@ import useObservable from 'react-use/lib/useObservable'; import type { TopNavMenuData } from 'src/plugins/navigation/public'; import type { FleetConfigType, FleetStartServices } from '../../plugin'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + RedirectAppLinks, +} from '../../../../../../src/plugins/kibana_react/public'; import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common'; import { PackageInstallProvider, useUrlModal } from '../integrations/hooks'; @@ -28,7 +30,6 @@ import { PackageInstallProvider, useUrlModal } from '../integrations/hooks'; import { ConfigContext, FleetStatusProvider, - IntraAppStateProvider, KibanaVersionContext, sendGetPermissionsCheck, sendSetup, @@ -215,43 +216,31 @@ export const FleetAppContext: React.FC<{ }> = memo( ({ children, startServices, config, history, kibanaVersion, extensions, routerHistory }) => { const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); - const [routerHistoryInstance] = useState(routerHistory || createHashHistory()); - // Sync our hash history with Kibana scoped history - useEffect(() => { - const unlistenParentHistory = history.listen(() => { - const newHash = createHashHistory(); - if (newHash.location.pathname !== routerHistoryInstance.location.pathname) { - routerHistoryInstance.replace(newHash.location.pathname + newHash.location.search || ''); - } - }); - - return unlistenParentHistory; - }, [history, routerHistoryInstance]); return ( - - - - - - - - - - + + + + + + + + + + {children} - - - - - - - - - + + + + + + + + + ); } ); @@ -277,7 +266,7 @@ const FleetTopNav = memo( defaultMessage: 'Fleet settings', }), iconType: 'gear', - run: () => (window.location.href = getModalHref('settings')), + run: () => services.application.navigateToUrl(getModalHref('settings')), }, ]; return ( @@ -327,7 +316,26 @@ export const AppRoutes = memo( - + { + // BWC < 7.15 Fleet was using a hash router: redirect old routes using hash + const shouldRedirectHash = location.pathname === '' && location.hash.length > 0; + if (!shouldRedirectHash) { + return ; + } + const pathname = location.hash.replace(/^#(\/fleet)?/, ''); + + return ( + + ); + }} + /> ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx index c0c425447e556..3b0ab9c62ca11 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/hooks/use_breadcrumbs.tsx @@ -150,18 +150,30 @@ const breadcrumbGetters: { }; export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) { - const { chrome, http } = useStartServices(); + const { chrome, http, application } = useStartServices(); const breadcrumbs = - breadcrumbGetters[page]?.(values).map((breadcrumb) => ({ - ...breadcrumb, - href: breadcrumb.href + breadcrumbGetters[page]?.(values).map((breadcrumb) => { + const href = breadcrumb.href ? http.basePath.prepend( - `${breadcrumb.useIntegrationsBasePath ? INTEGRATIONS_BASE_PATH : FLEET_BASE_PATH}#${ + `${breadcrumb.useIntegrationsBasePath ? INTEGRATIONS_BASE_PATH : FLEET_BASE_PATH}${ breadcrumb.href }` ) - : undefined, - })) || []; + : undefined; + return { + ...breadcrumb, + href, + onClick: href + ? (ev: React.MouseEvent) => { + if (ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey) { + return; + } + ev.preventDefault(); + application.navigateToUrl(href); + } + : undefined, + }; + }) || []; const docTitle: string[] = [...breadcrumbs] .reverse() .map((breadcrumb) => breadcrumb.text as string); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx index a624d8ced9180..c115089cccb1e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.test.tsx @@ -31,7 +31,7 @@ describe('when on the package policy create page', () => { beforeEach(() => { testRenderer = createFleetTestRendererMock(); mockApiCalls(testRenderer.startServices.http); - testRenderer.history.push(createPageUrlPath); + testRenderer.mountHistory.push(createPageUrlPath); }); describe('and Route state is provided via Fleet HashRouter', () => { @@ -43,7 +43,7 @@ describe('when on the package policy create page', () => { onCancelNavigateTo: [PLUGIN_ID, { path: '/cancel/url/here' }], }; - testRenderer.history.replace({ + testRenderer.mountHistory.replace({ pathname: createPageUrlPath, state: expectedRouteState, }); @@ -72,18 +72,18 @@ describe('when on the package policy create page', () => { expect(cancelButton.href).toBe(expectedRouteState.onCancelUrl); }); - it('should redirect via Fleet HashRouter when cancel link is clicked', () => { + it('should redirect via history when cancel link is clicked', () => { act(() => { cancelLink.click(); }); - expect(testRenderer.history.location.pathname).toBe('/cancel/url/here'); + expect(testRenderer.mountHistory.location.pathname).toBe('/cancel/url/here'); }); - it('should redirect via Fleet HashRouter when cancel Button (button bar) is clicked', () => { + it('should redirect via history when cancel Button (button bar) is clicked', () => { act(() => { cancelButton.click(); }); - expect(testRenderer.history.location.pathname).toBe('/cancel/url/here'); + expect(testRenderer.mountHistory.location.pathname).toBe('/cancel/url/here'); }); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx index 39340a21d349b..e19cb7b1ca5e8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx @@ -39,7 +39,7 @@ export const NoPackagePolicies = memo<{ policyId: string }>(({ policyId }) => { fill onClick={() => application.navigateToApp(INTEGRATIONS_PLUGIN_ID, { - path: `#${pagePathGetters.integrations_all()[1]}`, + path: pagePathGetters.integrations_all()[1], state: { forAgentPolicyId: policyId }, }) } 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 49af14b7234fa..0d2d8e1882183 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 @@ -199,7 +199,7 @@ export const PackagePoliciesTable: React.FunctionComponent = ({ iconType="refresh" onClick={() => { application.navigateToApp(INTEGRATIONS_PLUGIN_ID, { - path: `#${pagePathGetters.integrations_all()[1]}`, + path: pagePathGetters.integrations_all()[1], state: { forAgentPolicyId: agentPolicy.id }, }); }} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx index d8db44e28e4af..19f0216a39e03 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { HashRouter as Router, Switch, Route } from 'react-router-dom'; +import { Router, Switch, Route, useHistory } from 'react-router-dom'; import { FLEET_ROUTING_PATHS } from '../../constants'; import { useBreadcrumbs } from '../../hooks'; @@ -20,9 +20,10 @@ import { EditPackagePolicyPage } from './edit_package_policy_page'; export const AgentPolicyApp: React.FunctionComponent = () => { useBreadcrumbs('policies'); + const history = useHistory(); return ( - + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx index 52a4c9d17648b..494541a00fc0a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { HashRouter as Router, Route, Switch } from 'react-router-dom'; +import { Router, Route, Switch, useHistory } from 'react-router-dom'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPortal } from '@elastic/eui'; import { FLEET_ROUTING_PATHS } from '../../constants'; @@ -30,7 +30,7 @@ import { FleetServerUpgradeModal } from './components/fleet_server_upgrade_modal export const AgentsApp: React.FunctionComponent = () => { useBreadcrumbs('agent_list'); - + const history = useHistory(); const { agents } = useConfig(); const capabilities = useCapabilities(); @@ -118,7 +118,7 @@ export const AgentsApp: React.FunctionComponent = () => { ) : undefined; return ( - + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx index c660d3ed29767..51c8346a665cf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { HashRouter as Router, Route, Switch } from 'react-router-dom'; +import { Router, Route, Switch, useHistory } from 'react-router-dom'; import { FLEET_ROUTING_PATHS } from '../../constants'; import { DefaultLayout } from '../../layouts'; @@ -14,8 +14,10 @@ import { DefaultLayout } from '../../layouts'; import { DataStreamListPage } from './list_page'; export const DataStreamApp: React.FunctionComponent = () => { + const history = useHistory(); + return ( - + diff --git a/x-pack/plugins/fleet/public/applications/integrations/app.tsx b/x-pack/plugins/fleet/public/applications/integrations/app.tsx index ae59d33e44b82..f0c94b51677ee 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/app.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/app.tsx @@ -9,7 +9,6 @@ import React, { memo, useEffect, useState } from 'react'; import type { AppMountParameters } from 'kibana/public'; import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel, EuiPortal } from '@elastic/eui'; import type { History } from 'history'; -import { createHashHistory } from 'history'; import { Router, Redirect, Route, Switch } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -26,7 +25,10 @@ import { import type { FleetConfigType, FleetStartServices } from '../../plugin'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + RedirectAppLinks, +} from '../../../../../../src/plugins/kibana_react/public'; import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common'; import { AgentPolicyContextProvider, useUrlModal } from './hooks'; @@ -39,7 +41,7 @@ import type { UIExtensionsStorage } from './types'; import { EPMApp } from './sections/epm'; import { DefaultLayout, WithoutHeaderLayout } from './layouts'; import { PackageInstallProvider } from './hooks'; -import { useBreadcrumbs, IntraAppStateProvider, UIExtensionsContext } from './hooks'; +import { useBreadcrumbs, UIExtensionsContext } from './hooks'; const ErrorLayout = ({ children }: { children: JSX.Element }) => ( @@ -185,25 +187,12 @@ export const IntegrationsAppContext: React.FC<{ kibanaVersion: string; extensions: UIExtensionsStorage; /** For testing purposes only */ - routerHistory?: History; -}> = memo( - ({ children, startServices, config, history, kibanaVersion, extensions, routerHistory }) => { - const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); - const [routerHistoryInstance] = useState(routerHistory || createHashHistory()); - - // Sync our hash history with Kibana scoped history - useEffect(() => { - const unlistenParentHistory = history.listen(() => { - const newHash = createHashHistory(); - if (newHash.location.pathname !== routerHistoryInstance.location.pathname) { - routerHistoryInstance.replace(newHash.location.pathname + newHash.location.search || ''); - } - }); - - return unlistenParentHistory; - }, [history, routerHistoryInstance]); + routerHistory?: History; // TODO remove +}> = memo(({ children, startServices, config, history, kibanaVersion, extensions }) => { + const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); - return ( + return ( + @@ -212,15 +201,13 @@ export const IntegrationsAppContext: React.FC<{ - - - - - {children} - - - - + + + + {children} + + + @@ -229,9 +216,9 @@ export const IntegrationsAppContext: React.FC<{ - ); - } -); + + ); +}); export const AppRoutes = memo(() => { const { modal, setModal } = useUrlModal(); @@ -250,7 +237,26 @@ export const AppRoutes = memo(() => { - + { + // BWC < 7.15 Fleet was using a hash router: redirect old routes using hash + const shouldRedirectHash = location.pathname === '' && location.hash.length > 0; + if (!shouldRedirectHash) { + return ; + } + const pathname = location.hash.replace(/^#/, ''); + + return ( + + ); + }} + /> ); diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx index 19f72fdc69bba..63c8f1cbd318c 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx @@ -51,14 +51,23 @@ const breadcrumbGetters: { }; export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) { - const { chrome, http } = useStartServices(); + const { chrome, http, application } = useStartServices(); const breadcrumbs: ChromeBreadcrumb[] = - breadcrumbGetters[page]?.(values).map((breadcrumb) => ({ - ...breadcrumb, - href: breadcrumb.href - ? http.basePath.prepend(`${INTEGRATIONS_BASE_PATH}#${breadcrumb.href}`) - : undefined, - })) || []; + breadcrumbGetters[page]?.(values).map((breadcrumb) => { + const href = breadcrumb.href + ? http.basePath.prepend(`${INTEGRATIONS_BASE_PATH}${breadcrumb.href}`) + : undefined; + return { + ...breadcrumb, + href, + onClick: href + ? (ev: React.MouseEvent) => { + ev.preventDefault(); + application.navigateToUrl(href); + } + : undefined, + }; + }) || []; const docTitle: string[] = [...breadcrumbs] .reverse() .map((breadcrumb) => breadcrumb.text as string); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx index f436c248abd3c..31a3e2164a247 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx @@ -45,22 +45,22 @@ describe('when on integration detail', () => { )); - beforeEach(() => { + beforeEach(async () => { testRenderer = createIntegrationsTestRendererMock(); mockedApi = mockApiCalls(testRenderer.startServices.http); - testRenderer.history.push(detailPageUrlPath); + act(() => testRenderer.mountHistory.push(detailPageUrlPath)); }); afterEach(() => { cleanup(); - window.location.hash = '#/'; }); describe('and the package is installed', () => { beforeEach(() => render()); it('should display agent policy usage count', async () => { - await mockedApi.waitForApi(); + await act(() => mockedApi.waitForApi()); + expect(renderResult.queryByTestId('agentPolicyCount')).not.toBeNull(); }); @@ -105,11 +105,11 @@ describe('when on integration detail', () => { it('should redirect if custom url is accessed', () => { act(() => { - testRenderer.history.push( + testRenderer.mountHistory.push( pagePathGetters.integration_details_custom({ pkgkey: 'nginx-0.3.7' })[1] ); }); - expect(testRenderer.history.location.pathname).toEqual('/detail/nginx-0.3.7/overview'); + expect(testRenderer.mountHistory.location.pathname).toEqual('/detail/nginx-0.3.7/overview'); }); }); @@ -153,7 +153,7 @@ describe('when on integration detail', () => { it('should display custom content when tab is clicked', async () => { act(() => { - testRenderer.history.push( + testRenderer.mountHistory.push( pagePathGetters.integration_details_custom({ pkgkey: 'nginx-0.3.7' })[1] ); }); @@ -200,7 +200,7 @@ describe('when on integration detail', () => { it('should display custom assets when tab is clicked', async () => { act(() => { - testRenderer.history.push( + testRenderer.mountHistory.push( pagePathGetters.integration_details_assets({ pkgkey: 'nginx-0.3.7' })[1] ); }); @@ -215,7 +215,7 @@ describe('when on integration detail', () => { it('should link to the create page', () => { const addButton = renderResult.getByTestId('addIntegrationPolicyButton') as HTMLAnchorElement; expect(addButton.href).toEqual( - 'http://localhost/mock/app/fleet#/integrations/nginx-0.3.7/add-integration' + 'http://localhost/mock/app/fleet/integrations/nginx-0.3.7/add-integration' ); }); }); @@ -223,7 +223,7 @@ describe('when on integration detail', () => { describe('and on the Policies Tab', () => { const policiesTabURLPath = pagePathGetters.integration_details_policies({ pkgkey })[1]; beforeEach(() => { - testRenderer.history.push(policiesTabURLPath); + testRenderer.mountHistory.push(policiesTabURLPath); render(); }); @@ -238,7 +238,7 @@ describe('when on integration detail', () => { 'integrationNameLink' )[0] as HTMLAnchorElement; expect(firstPolicy.href).toEqual( - 'http://localhost/mock/app/integrations#/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc' + 'http://localhost/mock/app/integrations/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc' ); }); 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 21a139ad11baa..26869f8fea574 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 @@ -235,22 +235,18 @@ export function Detail() { redirectToPath = [ PLUGIN_ID, { - path: `#${ - pagePathGetters.policy_details({ - policyId: agentPolicyIdFromContext, - })[1] - }`, + path: pagePathGetters.policy_details({ + policyId: agentPolicyIdFromContext, + })[1], }, ]; } else { redirectToPath = [ INTEGRATIONS_PLUGIN_ID, { - path: `#${ - pagePathGetters.integration_details_policies({ - pkgkey, - })[1] - }`, + path: pagePathGetters.integration_details_policies({ + pkgkey, + })[1], }, ]; } @@ -260,16 +256,16 @@ export function Detail() { onCancelNavigateTo: [ INTEGRATIONS_PLUGIN_ID, { - path: currentPath, + path: pagePathGetters.integration_details_overview({ + pkgkey, + })[1], }, ], 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 - path: `#${path}`, + path, state: redirectBackRouteState, }); }, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx index 14f378bc379a6..3b161a375e7ce 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx @@ -53,7 +53,7 @@ const LatestVersionLink = ({ name, version }: { name: string; version: string }) pkgkey: `${name}-${version}`, }); return ( - + { - forRoute: string; - routeState?: S; -} - -const IntraAppStateContext = React.createContext({ forRoute: '' }); -const wasHandled = new WeakSet(); - -/** - * Provides a bridget between Kibana's ScopedHistory instance (normally used with BrowserRouter) - * and the Hash router used within the app in order to enable state to be used between kibana - * apps - */ -export const IntraAppStateProvider = memo<{ - kibanaScopedHistory: AppMountParameters['history']; - children: React.ReactNode; -}>(({ kibanaScopedHistory, children }) => { - const internalAppToAppState = useMemo(() => { - return { - forRoute: new URL(`${kibanaScopedHistory.location.hash.substr(1)}`, 'http://localhost') - .pathname, - routeState: kibanaScopedHistory.location.state as AnyIntraAppRouteState, - }; - }, [kibanaScopedHistory.location.state, kibanaScopedHistory.location.hash]); - return ( - - {children} - - ); -}); - /** * Retrieve UI Route state from the React Router History for the current URL location. * This state can be used by other Kibana Apps to influence certain behaviours in Ingest, for example, * redirecting back to an given Application after a craete action. */ -export function useIntraAppState(): - | IntraAppState['routeState'] - | undefined { +export function useIntraAppState(): S | undefined { const location = useLocation(); - const intraAppState = useContext(IntraAppStateContext); - if (!intraAppState) { - throw new Error('Hook called outside of IntraAppStateContext'); - } - return useMemo(() => { - // Due to the use of HashRouter in Ingest, we only want state to be returned - // once so that it does not impact navigation to the page from within the - // ingest app. side affect is that the browser back button would not work - // consistently either. - - if (location.pathname === intraAppState.forRoute && !wasHandled.has(intraAppState)) { - wasHandled.add(intraAppState); - return intraAppState.routeState as S; - } - // Default is to return the state in the Fleet HashRouter, in order to enable use of route state - // that is used via Kibana's ScopedHistory from within the Fleet HashRouter (ex. things like - // `core.application.navigateTo()` - // Once this https://github.com/elastic/kibana/issues/70358 is implemented (move to BrowserHistory - // using kibana's ScopedHistory), then this work-around can be removed. - return location.state as S; - }, [intraAppState, location.pathname, location.state]); + return location.state as S; } diff --git a/x-pack/plugins/fleet/public/hooks/use_link.ts b/x-pack/plugins/fleet/public/hooks/use_link.ts index 6917e0f5c3b8e..846ca9d0fdafa 100644 --- a/x-pack/plugins/fleet/public/hooks/use_link.ts +++ b/x-pack/plugins/fleet/public/hooks/use_link.ts @@ -27,7 +27,7 @@ export const useLink = () => { core.http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets/${path}`), getHref: (page: StaticPage | DynamicPage, values?: DynamicPagePathValues) => { const [basePath, path] = getSeparatePaths(page, values); - return core.http.basePath.prepend(`${basePath}#${path}`); + return core.http.basePath.prepend(`${basePath}${path}`); }, }; }; diff --git a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx index 71c9650709ee2..d0724545ee902 100644 --- a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx +++ b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx @@ -6,7 +6,7 @@ */ import type { History } from 'history'; -import { createMemoryHistory, createHashHistory } from 'history'; +import { createMemoryHistory } from 'history'; import React, { memo } from 'react'; import type { RenderOptions, RenderResult } from '@testing-library/react'; import { render as reactRender, act } from '@testing-library/react'; @@ -47,9 +47,10 @@ export const createFleetTestRendererMock = (): TestRenderer => { const basePath = '/mock'; const extensions: UIExtensionsStorage = {}; const startServices = createStartServices(basePath); + const history = createMemoryHistory({ initialEntries: [basePath] }); const testRendererMocks: TestRenderer = { - history: createHashHistory(), - mountHistory: new ScopedHistory(createMemoryHistory({ initialEntries: [basePath] }), basePath), + history, + mountHistory: new ScopedHistory(history, basePath), startServices, config: createConfigurationMock(), startInterface: createStartMock(extensions), @@ -89,7 +90,7 @@ export const createIntegrationsTestRendererMock = (): TestRenderer => { const extensions: UIExtensionsStorage = {}; const startServices = createStartServices(basePath); const testRendererMocks: TestRenderer = { - history: createHashHistory(), + history: createMemoryHistory(), mountHistory: new ScopedHistory(createMemoryHistory({ initialEntries: [basePath] }), basePath), startServices, config: createConfigurationMock(), diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index 0606334737a2a..2c723a3269737 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -104,6 +104,7 @@ export class FleetPlugin implements Plugin { const [coreStartServices, startDepsServices] = (await core.getStartServices()) as [ CoreStart, diff --git a/x-pack/plugins/fleet/public/search_provider.test.ts b/x-pack/plugins/fleet/public/search_provider.test.ts index 521337c9dda6b..8eee18710d477 100644 --- a/x-pack/plugins/fleet/public/search_provider.test.ts +++ b/x-pack/plugins/fleet/public/search_provider.test.ts @@ -92,7 +92,7 @@ describe('Package search provider', () => { title: 'test', type: 'integration', url: { - path: 'undefined#/detail/test-test/overview', + path: 'undefined/detail/test-test/overview', prependBasePath: false, }, }, @@ -102,7 +102,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, @@ -175,7 +175,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, @@ -231,7 +231,7 @@ describe('Package search provider', () => { title: 'test', type: 'integration', url: { - path: 'undefined#/detail/test-test/overview', + path: 'undefined/detail/test-test/overview', prependBasePath: false, }, }, @@ -241,7 +241,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, @@ -274,7 +274,7 @@ describe('Package search provider', () => { title: 'test1', type: 'integration', url: { - path: 'undefined#/detail/test1-test1/overview', + path: 'undefined/detail/test1-test1/overview', prependBasePath: false, }, }, diff --git a/x-pack/plugins/fleet/public/search_provider.ts b/x-pack/plugins/fleet/public/search_provider.ts index 9705f4da50f94..5f53c0a8e44ba 100644 --- a/x-pack/plugins/fleet/public/search_provider.ts +++ b/x-pack/plugins/fleet/public/search_provider.ts @@ -45,10 +45,8 @@ const toSearchResult = ( title: pkg.title, score: 80, url: { - // TODO: See https://github.com/elastic/kibana/issues/96134 for details about why we use '#' here. Below should be updated - // as part of migrating to non-hash based router. // prettier-ignore - path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}#${pagePathGetters.integration_details_overview({ pkgkey })[1]}`, + path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}${pagePathGetters.integration_details_overview({ pkgkey })[1]}`, prependBasePath: false, }, }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts index 148deda9aec76..05e1c2c4dca81 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts @@ -26,7 +26,7 @@ type EventHandlerCallback = MouseEventHandlerSee policies */ export const useNavigateToAppEventHandler = ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts index ca14dde18455b..e8fa53e2cf920 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/hooks.ts @@ -14,6 +14,7 @@ import { MANAGEMENT_STORE_GLOBAL_NAMESPACE, } from '../../../../common/constants'; import { useAppUrl } from '../../../../../common/lib/kibana'; +import { pagePathGetters } from '../../../../../../../fleet/public'; export function useEndpointSelector(selector: (state: EndpointState) => TSelected) { return useSelector(function (state: State) { @@ -47,7 +48,8 @@ export const useAgentDetailsIngestUrl = ( ): { url: string; appId: string; appPath: string } => { const { getAppUrl } = useAppUrl(); return useMemo(() => { - const appPath = `#/fleet/agents/${agentId}/activity`; + const appPath = pagePathGetters.agent_details_logs({ agentId })[1]; + return { url: `${getAppUrl({ appId: 'fleet' })}${appPath}`, appId: 'fleet', diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 584e6df1ff781..03df5d2bcbac7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -120,13 +120,13 @@ export const useEndpointActionItems = ( 'data-test-subj': 'agentPolicyLink', navigateAppId: 'fleet', navigateOptions: { - path: `#${ + path: `${ pagePathGetters.policy_details({ policyId: fleetAgentPolicies[endpointPolicyId], })[1] }`, }, - href: `${getAppUrl({ appId: 'fleet' })}#${ + href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.policy_details({ policyId: fleetAgentPolicies[endpointPolicyId], })[1] @@ -145,13 +145,13 @@ export const useEndpointActionItems = ( 'data-test-subj': 'agentDetailsLink', navigateAppId: 'fleet', navigateOptions: { - path: `#${ + path: `${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] }`, }, - href: `${getAppUrl({ appId: 'fleet' })}#${ + href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] @@ -169,17 +169,17 @@ export const useEndpointActionItems = ( 'data-test-subj': 'agentPolicyReassignLink', navigateAppId: 'fleet', navigateOptions: { - path: `#${ + path: `${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] - }/activity?openReassignFlyout=true`, + }?openReassignFlyout=true`, }, - href: `${getAppUrl({ appId: 'fleet' })}#${ + href: `${getAppUrl({ appId: 'fleet' })}${ pagePathGetters.agent_details({ agentId: fleetAgentId, })[1] - }/activity?openReassignFlyout=true`, + }?openReassignFlyout=true`, children: ( { }); it('navigates to the Ingest Agent Policy page', async () => { const agentPolicyLink = await renderResult.findByTestId('agentPolicyLink'); - expect(agentPolicyLink.getAttribute('href')).toEqual(`/app/fleet#/policies/${agentPolicyId}`); + expect(agentPolicyLink.getAttribute('href')).toEqual(`/app/fleet/policies/${agentPolicyId}`); }); it('navigates to the Ingest Agent Details page', async () => { const agentDetailsLink = await renderResult.findByTestId('agentDetailsLink'); - expect(agentDetailsLink.getAttribute('href')).toEqual(`/app/fleet#/agents/${agentId}`); + expect(agentDetailsLink.getAttribute('href')).toEqual(`/app/fleet/agents/${agentId}`); }); it('navigates to the Ingest Agent Details page with policy reassign', async () => { const agentPolicyReassignLink = await renderResult.findByTestId('agentPolicyReassignLink'); expect(agentPolicyReassignLink.getAttribute('href')).toEqual( - `/app/fleet#/agents/${agentId}/activity?openReassignFlyout=true` + `/app/fleet/agents/${agentId}?openReassignFlyout=true` ); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index c78d4ca6af634..74f5b15a72727 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -156,7 +156,7 @@ export const EndpointList = () => { const handleCreatePolicyClick = useNavigateToAppEventHandler( 'fleet', { - path: `#/integrations/${ + path: `/integrations/${ endpointPackageVersion ? `/endpoint-${endpointPackageVersion}` : '' }/add-integration`, state: { @@ -203,7 +203,7 @@ export const EndpointList = () => { const handleDeployEndpointsClick = useNavigateToAppEventHandler( 'fleet', { - path: `#/policies/${selectedPolicyId}?openEnrollmentFlyout=true`, + path: `/policies/${selectedPolicyId}?openEnrollmentFlyout=true`, state: { onDoneNavigateTo: [ 'securitySolution', diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx index 7af9f84ad0875..2d21ec9565476 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx @@ -63,7 +63,7 @@ describe('OverviewEmpty', () => { fill: false, label: 'Add Endpoint Security', onClick: undefined, - url: `#/integrations/endpoint-${endpointPackageVersion}/add-integration`, + url: `/integrations/endpoint-${endpointPackageVersion}/add-integration`, }, }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx index c75438e18f5d5..6f885b348cdeb 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx @@ -36,7 +36,7 @@ const OverviewEmptyComponent: React.FC = () => { const endpointIntegrationUrlPath = endpointPackageVersion ? `/endpoint-${endpointPackageVersion}/add-integration` : ''; - const endpointIntegrationUrl = `#/integrations${endpointIntegrationUrlPath}`; + const endpointIntegrationUrl = `/integrations${endpointIntegrationUrlPath}`; const handleEndpointClick = useNavigateToAppEventHandler('fleet', { path: endpointIntegrationUrl, }); 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 3321234a345e4..81ddaf06febd9 100644 --- a/x-pack/test/functional/page_objects/synthetics_integration_page.ts +++ b/x-pack/test/functional/page_objects/synthetics_integration_page.ts @@ -24,25 +24,17 @@ export function SyntheticsIntegrationPageProvider({ * */ async navigateToPackagePage(packageVersion: string) { - await pageObjects.common.navigateToUrl( + await pageObjects.common.navigateToUrlWithBrowserHistory( 'fleet', - `/integrations/synthetics-${packageVersion}/add-integration`, - { - shouldUseHashForSubUrl: true, - useActualUrl: true, - } + `/integrations/synthetics-${packageVersion}/add-integration` ); await pageObjects.header.waitUntilLoadingHasFinished(); }, async navigateToPackageEditPage(packageId: string, agentId: string) { - await pageObjects.common.navigateToUrl( + await pageObjects.common.navigateToUrlWithBrowserHistory( 'fleet', - `/policies/${agentId}/edit-integration/${packageId}`, - { - shouldUseHashForSubUrl: true, - useActualUrl: true, - } + `/policies/${agentId}/edit-integration/${packageId}` ); await pageObjects.header.waitUntilLoadingHasFinished(); }, diff --git a/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts b/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts index 5abc842ddc9c9..81e868c5a38cb 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts @@ -17,9 +17,10 @@ export function FleetIntegrations({ getService, getPageObjects }: FtrProviderCon return { async navigateToIntegrationDetails(pkgkey: string) { - await pageObjects.common.navigateToApp(INTEGRATIONS_PLUGIN_ID, { - hash: pagePathGetters.integration_details_overview({ pkgkey })[1], - }); + await pageObjects.common.navigateToUrlWithBrowserHistory( + INTEGRATIONS_PLUGIN_ID, + pagePathGetters.integration_details_overview({ pkgkey })[1] + ); }, async integrationDetailCustomTabExistsOrFail() {