core-api: migrate RoutingContext to use version bridge
Co-authored-by: Juan Lulkin <jmaiz@spotify.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -14,9 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
import React, { PropsWithChildren, ReactElement } from 'react';
|
||||
import React, {
|
||||
PropsWithChildren,
|
||||
ReactElement,
|
||||
useContext,
|
||||
Context,
|
||||
} from 'react';
|
||||
import { MemoryRouter, Routes } from 'react-router-dom';
|
||||
import { render } from '@testing-library/react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { VersionedValue } from '../lib/versionedValues';
|
||||
import { getGlobalSingleton } from '../lib/globalObject';
|
||||
import { createRoutableExtension } from '../extensions';
|
||||
import {
|
||||
childDiscoverer,
|
||||
@@ -32,6 +40,7 @@ import {
|
||||
import { validateRoutes } from './validation';
|
||||
import { useRouteRef, RoutingProvider } from './hooks';
|
||||
import { createRouteRef, RouteRefConfig } from './RouteRef';
|
||||
import { RouteResolver } from './RouteResolver';
|
||||
import { createExternalRouteRef } from './ExternalRouteRef';
|
||||
import { AnyRouteRef, RouteFunc, RouteRef, ExternalRouteRef } from './types';
|
||||
|
||||
@@ -309,3 +318,55 @@ describe('discovery', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('v1 consumer', () => {
|
||||
const RoutingContext = getGlobalSingleton<
|
||||
Context<VersionedValue<{ 1: RouteResolver }>>
|
||||
>('routing-context');
|
||||
|
||||
function useMockRouteRefV1(
|
||||
routeRef: AnyRouteRef,
|
||||
location: string,
|
||||
): RouteFunc<any> | undefined {
|
||||
const resolver = useContext(RoutingContext)?.atVersion(1);
|
||||
if (!resolver) {
|
||||
throw new Error('no impl');
|
||||
}
|
||||
return resolver.resolve(routeRef, location);
|
||||
}
|
||||
|
||||
it('should resolve routes', () => {
|
||||
const routeRef1 = createRouteRef({ id: 'ref1' });
|
||||
const routeRef2 = createRouteRef({ id: 'ref2' });
|
||||
const routeRef3 = createRouteRef({ id: 'ref3', params: ['x'] });
|
||||
|
||||
const renderedHook = renderHook(
|
||||
({ routeRef }) => useMockRouteRefV1(routeRef, '/'),
|
||||
{
|
||||
initialProps: {
|
||||
routeRef: routeRef1 as AnyRouteRef,
|
||||
},
|
||||
wrapper: ({ children }) => (
|
||||
<RoutingProvider
|
||||
routePaths={
|
||||
new Map<RouteRef<any>, string>([
|
||||
[routeRef2, '/foo'],
|
||||
[routeRef3, '/bar/:x'],
|
||||
])
|
||||
}
|
||||
routeParents={new Map()}
|
||||
routeObjects={[]}
|
||||
routeBindings={new Map()}
|
||||
children={children}
|
||||
/>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
expect(renderedHook.result.current).toBe(undefined);
|
||||
renderedHook.rerender({ routeRef: routeRef2 });
|
||||
expect(renderedHook.result.current?.()).toBe('/foo');
|
||||
renderedHook.rerender({ routeRef: routeRef3 });
|
||||
expect(renderedHook.result.current?.({ x: 'my-x' })).toBe('/bar/my-x');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createContext, ReactNode, useContext, useMemo } from 'react';
|
||||
import React, {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useContext,
|
||||
useMemo,
|
||||
Context,
|
||||
} from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import {
|
||||
BackstageRouteObject,
|
||||
@@ -25,8 +31,16 @@ import {
|
||||
RouteFunc,
|
||||
} from './types';
|
||||
import { RouteResolver } from './RouteResolver';
|
||||
import {
|
||||
VersionedValue,
|
||||
createVersionedValueMap,
|
||||
} from '../lib/versionedValues';
|
||||
import { setGlobalSingleton, getGlobalSingleton } from '../lib/globalObject';
|
||||
|
||||
const RoutingContext = createContext<RouteResolver | undefined>(undefined);
|
||||
type RoutingContextType = VersionedValue<{ 1: RouteResolver }> | undefined;
|
||||
const RoutingContext = createContext<RoutingContextType>(undefined);
|
||||
|
||||
setGlobalSingleton('routing-context', RoutingContext);
|
||||
|
||||
export function useRouteRef<Optional extends boolean, Params extends AnyParams>(
|
||||
routeRef: ExternalRouteRef<Params, Optional>,
|
||||
@@ -41,14 +55,20 @@ export function useRouteRef<Params extends AnyParams>(
|
||||
| ExternalRouteRef<Params, any>,
|
||||
): RouteFunc<Params> | undefined {
|
||||
const sourceLocation = useLocation();
|
||||
const resolver = useContext(RoutingContext);
|
||||
const versionedContext = useContext(
|
||||
getGlobalSingleton<Context<RoutingContextType>>('routing-context'),
|
||||
);
|
||||
const resolver = versionedContext?.atVersion(1);
|
||||
const routeFunc = useMemo(
|
||||
() => resolver && resolver.resolve(routeRef, sourceLocation),
|
||||
[resolver, routeRef, sourceLocation],
|
||||
);
|
||||
|
||||
if (!routeFunc && !resolver) {
|
||||
throw new Error('No route resolver found in context');
|
||||
if (!versionedContext) {
|
||||
throw new Error('useRouteRef used outside of routing context');
|
||||
}
|
||||
if (!resolver) {
|
||||
throw new Error('RoutingContext v1 not available');
|
||||
}
|
||||
|
||||
const isOptional = 'optional' in routeRef && routeRef.optional;
|
||||
@@ -80,8 +100,10 @@ export const RoutingProvider = ({
|
||||
routeObjects,
|
||||
routeBindings,
|
||||
);
|
||||
|
||||
const versionedValue = createVersionedValueMap({ 1: resolver });
|
||||
return (
|
||||
<RoutingContext.Provider value={resolver}>
|
||||
<RoutingContext.Provider value={versionedValue}>
|
||||
{children}
|
||||
</RoutingContext.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user