Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ export class ApplicationService {
setAppActionMenu={this.setAppActionMenu}
setIsMounting={(isMounting) => httpLoadingCount$.next(isMounting ? 1 : 0)}
hasCustomBranding$={this.hasCustomBranding$}
appId$={this.currentAppId$}
/>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@

import React, { FunctionComponent, useMemo } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { Router, Routes, Route } from '@kbn/shared-ux-router';
import { Router, Routes, Route, RouterProvider } from '@kbn/shared-ux-router';
import { History } from 'history';
import { EMPTY, Observable } from 'rxjs';
import { BehaviorSubject, EMPTY, Observable } from 'rxjs';
import useObservable from 'react-use/lib/useObservable';

import type { CoreTheme } from '@kbn/core-theme-browser';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import { type AppLeaveHandler, AppStatus } from '@kbn/core-application-browser';
import { KibanaErrorBoundary, KibanaErrorBoundaryProvider } from '@kbn/shared-ux-error-boundary';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import { ExecutionContextService } from '@kbn/core-execution-context-browser-internal';
import type { Mounter } from '../types';
import { AppContainer } from './app_container';
import { CoreScopedHistory } from '../scoped_history';
Expand All @@ -32,6 +33,7 @@ interface Props {
setAppActionMenu: (appId: string, mount: MountPoint | undefined) => void;
setIsMounting: (isMounting: boolean) => void;
hasCustomBranding$?: Observable<boolean>;
appId$: BehaviorSubject<string | undefined>;
}

interface Params {
Expand All @@ -48,6 +50,7 @@ export const AppRouter: FunctionComponent<Props> = ({
appStatuses$,
setIsMounting,
hasCustomBranding$,
appId$,
}) => {
const appStatuses = useObservable(appStatuses$, new Map());
const createScopedHistory = useMemo(
Expand All @@ -56,66 +59,69 @@ export const AppRouter: FunctionComponent<Props> = ({
);

const showPlainSpinner = useObservable(hasCustomBranding$ ?? EMPTY, false);
const { context$, get, set, clear } = new ExecutionContextService().start({ curApp$: appId$ });

return (
<KibanaErrorBoundaryProvider analytics={analytics}>
<KibanaErrorBoundary>
<Router history={history}>
<Routes>
{[...mounters].map(([appId, mounter]) => (
<RouterProvider context$={context$} get={get} set={set} clear={clear}>
<Router history={history}>
<Routes>
{[...mounters].map(([appId, mounter]) => (
<Route
key={mounter.appRoute}
path={mounter.appRoute}
exact={mounter.exactRoute}
render={({ match: { path } }) => (
<AppContainer
appPath={path}
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
createScopedHistory={createScopedHistory}
{...{
appId,
mounter,
setAppLeaveHandler,
setAppActionMenu,
setIsMounting,
theme$,
showPlainSpinner,
}}
/>
)}
/>
))}
{/* handler for legacy apps and used as a catch-all to display 404 page on not existing /app/appId apps*/}
<Route
key={mounter.appRoute}
path={mounter.appRoute}
exact={mounter.exactRoute}
render={({ match: { path } }) => (
<AppContainer
appPath={path}
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
createScopedHistory={createScopedHistory}
{...{
appId,
mounter,
setAppLeaveHandler,
setAppActionMenu,
setIsMounting,
theme$,
showPlainSpinner,
}}
/>
)}
path="/app/:appId"
render={({
match: {
params: { appId },
url,
},
}: RouteComponentProps<Params>) => {
// the id/mounter retrieval can be removed once #76348 is addressed
const [id, mounter] = mounters.has(appId) ? [appId, mounters.get(appId)] : [];
return (
<AppContainer
appPath={url}
appId={id ?? appId}
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
createScopedHistory={createScopedHistory}
{...{
mounter,
setAppLeaveHandler,
setAppActionMenu,
setIsMounting,
theme$,
showPlainSpinner,
}}
/>
);
}}
/>
))}
{/* handler for legacy apps and used as a catch-all to display 404 page on not existing /app/appId apps*/}
<Route
path="/app/:appId"
render={({
match: {
params: { appId },
url,
},
}: RouteComponentProps<Params>) => {
// the id/mounter retrieval can be removed once #76348 is addressed
const [id, mounter] = mounters.has(appId) ? [appId, mounters.get(appId)] : [];
return (
<AppContainer
appPath={url}
appId={id ?? appId}
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
createScopedHistory={createScopedHistory}
{...{
mounter,
setAppLeaveHandler,
setAppActionMenu,
setIsMounting,
theme$,
showPlainSpinner,
}}
/>
);
}}
/>
</Routes>
</Router>
</Routes>
</Router>
</RouterProvider>
</KibanaErrorBoundary>
</KibanaErrorBoundaryProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@kbn/core-analytics-browser",
"@kbn/shared-ux-router",
"@kbn/shared-ux-error-boundary",
"@kbn/core-execution-context-browser-internal",
],
"exclude": [
"target/**/*",
Expand Down
1 change: 1 addition & 0 deletions packages/shared-ux/router/impl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
export { Route } from './route';
export { HashRouter, BrowserRouter, MemoryRouter, Router } from './router';
export { Routes } from './routes';
export { RouterProvider } from './services';
2 changes: 1 addition & 1 deletion packages/shared-ux/router/impl/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const Route = <T extends {}>({
* The match propagator that is part of the Route
*/
export const MatchPropagator = () => {
const { executionContext } = useKibanaSharedUX().services;
const { executionContext } = useKibanaSharedUX().services.coreStart?.http;
const match = useRouteMatch();

useSharedUXExecutionContext(executionContext, {
Expand Down
27 changes: 27 additions & 0 deletions packages/shared-ux/router/impl/router.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React from 'react';
import { render } from '@testing-library/react';
import { RouterProvider } from './services';
import { KibanaSharedUXRouterProviderDeps } from '@kbn/shared-ux-router-types';
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';

describe('<RouterProvider', () => {
let mockContext$: KibanaSharedUXRouterProviderDeps['context$'];

beforeEach(() => {
mockContext$ = executionContextServiceMock;
});

it('should render', async () => {
const { findByTestId } = render(
<RouterProvider context$={mockContext$} set={jest.fn()} get={jest.fn()} clear={jest.fn()} />
);
expect(findByTestId('router-shared-ux')).toBeTruthy();
});
});
2 changes: 1 addition & 1 deletion packages/shared-ux/router/impl/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const MemoryRouter = ({ children, ...props }: MemoryRouterProps) => (
);

export const Router = ({ children, ...props }: RouterProps) => (
<ReactRouter {...props}>
<ReactRouter {...props} data-test-subj="router-shared-ux">
<CompatRouter>{children}</CompatRouter>
</ReactRouter>
);
73 changes: 0 additions & 73 deletions packages/shared-ux/router/impl/services.ts

This file was deleted.

53 changes: 53 additions & 0 deletions packages/shared-ux/router/impl/services.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React, { createContext, useContext, FC, PropsWithChildren, useMemo } from 'react';
import {
KibanaSharedUXRouterProviderDeps,
SharedUXRouterServices,
} from '@kbn/shared-ux-router-types';

const Context = createContext<SharedUXRouterServices | null>(null);

export const SharedUXRouterDepsProvider: FC<PropsWithChildren<SharedUXRouterServices>> = ({
children,
services,
}) => <Context.Provider value={{ services }}>{children}</Context.Provider>;

export const RouterProvider: FC<PropsWithChildren<KibanaSharedUXRouterProviderDeps>> = ({
children,
context$,
get,
set,
clear,
}) => {
const parentContext = useContext(Context);
const value: SharedUXRouterServices = useMemo(() => {
if (parentContext) {
return parentContext;
}

return {
services: { get, set, clear, context$ },
};
}, [clear, context$, get, parentContext, set]);
return <Context.Provider value={value}>{children}</Context.Provider>;
};

/** Utility that provides context
* @internal
*/
export function useKibanaSharedUX(): SharedUXRouterServices {
const context = useContext(Context);
if (!context) {
throw new Error(
'Kibana Shared UX Context is missing. Ensure your component or React root is wrapped with Kibana Shared UX Context.'
);
}
return context;
}
2 changes: 2 additions & 0 deletions packages/shared-ux/router/impl/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"**/*",
],
"kbn_references": [
"@kbn/shared-ux-router-types",
"@kbn/core-execution-context-server-mocks",
],
"exclude": [
"target/**/*",
Expand Down
6 changes: 4 additions & 2 deletions packages/shared-ux/router/impl/use_execution_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import useDeepCompareEffect from 'react-use/lib/useDeepCompareEffect';
import { SharedUXExecutionContextSetup } from './services';
import { SharedUXExecutionContextStart } from './services';
import { SharedUXExecutionContext } from './types';

/**
Expand All @@ -16,7 +16,9 @@ import { SharedUXExecutionContext } from './types';
* @param context
*/
export function useSharedUXExecutionContext(
executionContext: SharedUXExecutionContextSetup | undefined,
executionContext:
| SharedUXExecutionContextStart['coreStart']['http']['executionContext']
| undefined,
context: SharedUXExecutionContext
) {
useDeepCompareEffect(() => {
Expand Down
Loading