From 7d30365c67795ac27b50d558793360ff0a176d75 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 9 Mar 2021 12:19:32 +0100 Subject: [PATCH] core-api: migrate RoutingContext to use version bridge Co-authored-by: Juan Lulkin Signed-off-by: Patrik Oldsberg --- packages/core-api/src/routing/hooks.test.tsx | 65 +++++++++++++++++++- packages/core-api/src/routing/hooks.tsx | 34 ++++++++-- 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/packages/core-api/src/routing/hooks.test.tsx b/packages/core-api/src/routing/hooks.test.tsx index 9a65286fd6..d16f9808ab 100644 --- a/packages/core-api/src/routing/hooks.test.tsx +++ b/packages/core-api/src/routing/hooks.test.tsx @@ -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> + >('routing-context'); + + function useMockRouteRefV1( + routeRef: AnyRouteRef, + location: string, + ): RouteFunc | 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 }) => ( + , 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'); + }); +}); diff --git a/packages/core-api/src/routing/hooks.tsx b/packages/core-api/src/routing/hooks.tsx index 2e65efaba6..b8690d867a 100644 --- a/packages/core-api/src/routing/hooks.tsx +++ b/packages/core-api/src/routing/hooks.tsx @@ -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(undefined); +type RoutingContextType = VersionedValue<{ 1: RouteResolver }> | undefined; +const RoutingContext = createContext(undefined); + +setGlobalSingleton('routing-context', RoutingContext); export function useRouteRef( routeRef: ExternalRouteRef, @@ -41,14 +55,20 @@ export function useRouteRef( | ExternalRouteRef, ): RouteFunc | undefined { const sourceLocation = useLocation(); - const resolver = useContext(RoutingContext); + const versionedContext = useContext( + getGlobalSingleton>('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 ( - + {children} );