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:
Patrik Oldsberg
2021-03-09 12:19:32 +01:00
parent 0095873736
commit 7d30365c67
2 changed files with 91 additions and 8 deletions
+63 -2
View File
@@ -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');
});
});
+28 -6
View File
@@ -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>
);