Skip to content
Merged
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
29 changes: 19 additions & 10 deletions packages/kbn-typed-react-router-config/src/create_router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { deepExactRt as deepExactRtTyped, mergeRt as mergeRtTyped } from '@
import { deepExactRt as deepExactRtNonTyped } from '@kbn/io-ts-utils/target_node/deep_exact_rt';
// @ts-expect-error
import { mergeRt as mergeRtNonTyped } from '@kbn/io-ts-utils/target_node/merge_rt';
import { Route, Router } from './types';
import { FlattenRoutesOf, Route, Router } from './types';

const deepExactRt: typeof deepExactRtTyped = deepExactRtNonTyped;
const mergeRt: typeof mergeRtTyped = mergeRtNonTyped;
Expand Down Expand Up @@ -51,6 +51,20 @@ export function createRouter<TRoutes extends Route[]>(routes: TRoutes): Router<T
return reactRouterConfig;
}

function getRoutesToMatch(path: string) {
const matches = matchRoutesConfig(reactRouterConfigs, toReactRouterPath(path));

if (!matches.length) {
throw new Error(`No matching route found for ${path}`);
}

const matchedRoutes = matches.map((match) => {
return routesByReactRouterConfig.get(match.route)!;
});

return matchedRoutes;
}

const matchRoutes = (...args: any[]) => {
let optional: boolean = false;

Expand Down Expand Up @@ -142,15 +156,7 @@ export function createRouter<TRoutes extends Route[]>(routes: TRoutes): Router<T
})
.join('/');

const matches = matchRoutesConfig(reactRouterConfigs, toReactRouterPath(path));

if (!matches.length) {
throw new Error(`No matching route found for ${path}`);
}

const matchedRoutes = matches.map((match) => {
return routesByReactRouterConfig.get(match.route)!;
});
const matchedRoutes = getRoutesToMatch(path);

const validationType = mergeRt(
...(compact(
Expand Down Expand Up @@ -200,5 +206,8 @@ export function createRouter<TRoutes extends Route[]>(routes: TRoutes): Router<T
getRoutePath: (route) => {
return reactRouterConfigsByRoute.get(route)!.path as string;
},
getRoutesToMatch: (path: string) => {
return getRoutesToMatch(path) as unknown as FlattenRoutesOf<TRoutes>;
},
};
}
21 changes: 18 additions & 3 deletions packages/kbn-typed-react-router-config/src/outlet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,24 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { useCurrentRoute } from './use_current_route';
import React, { createContext, useContext } from 'react';

const OutletContext = createContext<{ element?: React.ReactElement } | undefined>(undefined);

export function OutletContextProvider({
element,
children,
}: {
element: React.ReactElement;
children: React.ReactNode;
}) {
return <OutletContext.Provider value={{ element }}>{children}</OutletContext.Provider>;
}

export function Outlet() {
const { element } = useCurrentRoute();
return element;
const outletContext = useContext(OutletContext);
if (!outletContext) {
throw new Error('Outlet context not available');
}
return outletContext.element || null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function RouterProvider({
}: {
router: Router<Route[]>;
history: History;
children: React.ReactElement;
children: React.ReactNode;
}) {
return (
<ReactRouter history={history}>
Expand Down
39 changes: 22 additions & 17 deletions packages/kbn-typed-react-router-config/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ interface PlainRoute {
children?: PlainRoute[];
params?: t.Type<any>;
defaults?: Record<string, Record<string, string>>;
pre?: ReactElement;
}

interface ReadonlyPlainRoute {
Expand All @@ -155,6 +156,7 @@ interface ReadonlyPlainRoute {
readonly children?: readonly ReadonlyPlainRoute[];
readonly params?: t.Type<any>;
readonly defaults?: Record<string, Record<string, string>>;
pre?: ReactElement;
}

export type Route = PlainRoute | ReadonlyPlainRoute;
Expand Down Expand Up @@ -209,6 +211,10 @@ export type TypeAsArgs<TObject> = keyof TObject extends never
? [TObject] | []
: [TObject];

export type FlattenRoutesOf<TRoutes extends Route[]> = Array<
Omit<ValuesType<MapRoutes<TRoutes>>, 'parents'>
>;

export interface Router<TRoutes extends Route[]> {
matchRoutes<TPath extends PathsOf<TRoutes>>(
path: TPath,
Expand Down Expand Up @@ -245,6 +251,7 @@ export interface Router<TRoutes extends Route[]> {
...args: TypeAsArgs<TypeOf<TRoutes, TPath, false>>
): string;
getRoutePath(route: Route): string;
getRoutesToMatch(path: string): FlattenRoutesOf<TRoutes>;
}

type AppendPath<
Expand All @@ -256,23 +263,21 @@ type MaybeUnion<T extends Record<string, any>, U extends Record<string, any>> =
[key in keyof U]: key extends keyof T ? T[key] | U[key] : U[key];
};

type MapRoute<TRoute extends Route, TParents extends Route[] = []> = TRoute extends Route
? MaybeUnion<
{
[key in TRoute['path']]: TRoute & { parents: TParents };
},
TRoute extends { children: Route[] }
? MaybeUnion<
MapRoutes<TRoute['children'], [...TParents, TRoute]>,
{
[key in AppendPath<TRoute['path'], '*'>]: ValuesType<
MapRoutes<TRoute['children'], [...TParents, TRoute]>
>;
}
>
: {}
>
: {};
type MapRoute<TRoute extends Route, TParents extends Route[] = []> = MaybeUnion<
{
[key in TRoute['path']]: TRoute & { parents: TParents };
},
TRoute extends { children: Route[] }
? MaybeUnion<
MapRoutes<TRoute['children'], [...TParents, TRoute]>,
{
[key in AppendPath<TRoute['path'], '*'>]: ValuesType<
MapRoutes<TRoute['children'], [...TParents, TRoute]>
>;
}
>
: {}
>;

type MapRoutes<TRoutes, TParents extends Route[] = []> = TRoutes extends [Route]
? MapRoute<TRoutes[0], TParents>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import React, { createContext, useContext } from 'react';
import { OutletContextProvider } from './outlet';
import { RouteMatch } from './types';

const CurrentRouteContext = createContext<
Expand All @@ -23,7 +24,7 @@ export const CurrentRouteContextProvider = ({
}) => {
return (
<CurrentRouteContext.Provider value={{ match, element }}>
{children}
<OutletContextProvider element={element}>{children}</OutletContextProvider>
</CurrentRouteContext.Provider>
);
};
Expand Down
12 changes: 8 additions & 4 deletions packages/kbn-typed-react-router-config/src/use_match_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { RouteMatch } from './types';
import { useRouter } from './use_router';
Expand All @@ -14,7 +14,11 @@ export function useMatchRoutes(path?: string): RouteMatch[] {
const router = useRouter();
const location = useLocation();

return typeof path === 'undefined'
? router.matchRoutes(location)
: router.matchRoutes(path as never, location);
const routeMatches = useMemo(() => {
return typeof path === 'undefined'
? router.matchRoutes(location)
: router.matchRoutes(path as never, location);
}, [path, router, location]);

return routeMatches;
}
2 changes: 1 addition & 1 deletion packages/kbn-typed-react-router-config/src/use_router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const RouterContextProvider = ({
children,
}: {
router: Router<Route[]>;
children: React.ReactElement;
children: React.ReactNode;
}) => <RouterContext.Provider value={router}>{children}</RouterContext.Provider>;

export function useRouter(): Router<Route[]> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import { EuiFlexGroup, EuiTitle, EuiFlexItem } from '@elastic/eui';
import { RumOverview } from '../RumDashboard';
import { CsmSharedContextProvider } from './CsmSharedContext';
import { WebApplicationSelect } from './Panels/WebApplicationSelect';
import { DatePicker } from '../../shared/DatePicker';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
import { UxEnvironmentFilter } from '../../shared/EnvironmentFilter';
import { UserPercentile } from './UserPercentile';
import { useBreakpoints } from '../../../hooks/use_breakpoints';
import { KibanaPageTemplateProps } from '../../../../../../../src/plugins/kibana_react/public';
import { useHasRumData } from './hooks/useHasRumData';
import { RumDatePicker } from './rum_datepicker';
import { EmptyStateLoading } from './empty_state_loading';

export const DASHBOARD_LABEL = i18n.translate('xpack.apm.ux.title', {
Expand Down Expand Up @@ -88,7 +88,7 @@ function PageHeader() {
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem style={{ alignItems: 'flex-end', ...datePickerStyle }}>
<DatePicker />
<RumDatePicker />
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup wrap>
Expand Down
Loading