From f9ec4e46e39659c1e26986aa6a88cf4268982614 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 1 Sep 2022 17:29:37 +0200 Subject: [PATCH] core-app-api: allow path props in route elements Signed-off-by: Patrik Oldsberg --- .changeset/cool-cameras-count.md | 5 ++++ .../src/routing/collectors.stable.test.tsx | 25 ++++++++--------- .../core-app-api/src/routing/collectors.tsx | 27 +++++++++---------- 3 files changed, 30 insertions(+), 27 deletions(-) create mode 100644 .changeset/cool-cameras-count.md diff --git a/.changeset/cool-cameras-count.md b/.changeset/cool-cameras-count.md new file mode 100644 index 0000000000..02c8ffc2bd --- /dev/null +++ b/.changeset/cool-cameras-count.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +When using React Router v6 stable, it is now possible for components within the `Route` element tree to have `path` props, although they will be ignored. diff --git a/packages/core-app-api/src/routing/collectors.stable.test.tsx b/packages/core-app-api/src/routing/collectors.stable.test.tsx index 2324ea5b69..f8f71a1a9a 100644 --- a/packages/core-app-api/src/routing/collectors.stable.test.tsx +++ b/packages/core-app-api/src/routing/collectors.stable.test.tsx @@ -453,18 +453,19 @@ describe('discovery', () => { ); }); - it('should throw elements within element prop contains a path', () => { - expect(() => { - traverseElementTree({ - root: } />, - discoverers: [childDiscoverer, routeElementDiscoverer], - collectors: { - routing: routingV2Collector, - }, - }); - }).toThrow( - 'Elements within the element prop tree may not have paths, found "bar"', - ); + it('should ignore path props within route elements', () => { + const { routing } = traverseElementTree({ + root: } />, + discoverers: [childDiscoverer, routeElementDiscoverer], + collectors: { + routing: routingV2Collector, + }, + }); + expect(sortedEntries(routing.paths)).toEqual([[ref1, 'foo']]); + expect(sortedEntries(routing.parents)).toEqual([[ref1, undefined]]); + expect(routing.objects).toEqual([ + routeObj('foo', [ref1], [], undefined, plugin), + ]); }); it('should throw when a routable extension does not have a path set', () => { diff --git a/packages/core-app-api/src/routing/collectors.tsx b/packages/core-app-api/src/routing/collectors.tsx index e5d71f5117..815ae534f1 100644 --- a/packages/core-app-api/src/routing/collectors.tsx +++ b/packages/core-app-api/src/routing/collectors.tsx @@ -53,6 +53,8 @@ interface RoutingV2CollectorContext { isElementAncestor?: boolean; } +// This collects all the mount points and their plugins within an element tree. +// Unlike regular traversal this ignores all other things, like path props and mount point gatherers. function collectSubTree( node: ReactNode, entries = new Array<{ routeRef: RouteRef; plugin?: BackstagePlugin }>(), @@ -62,12 +64,6 @@ function collectSubTree( return; } - if (element.props.path) { - throw new Error( - `Elements within the element prop tree may not have paths, found "${element.props.path}"`, - ); - } - const routeRef = getComponentData(element, 'core.mountPoint'); if (routeRef) { const plugin = getComponentData(element, 'core.plugin'); @@ -87,6 +83,16 @@ export const routingV2Collector = createCollector( objects: new Array(), }), (acc, node, parent, ctx?: RoutingV2CollectorContext) => { + // If we're in an element prop, ignore everything + if (ctx?.isElementAncestor) { + return ctx; + } + + // Start ignoring everything if we enter an element prop + if (parent?.props.element === node) { + return { ...ctx, isElementAncestor: true }; + } + const pathProp: unknown = node.props?.path; const mountPoint = getComponentData(node, 'core.mountPoint'); @@ -98,15 +104,6 @@ export const routingV2Collector = createCollector( ); } - // If we're in an element prop, ignore everything - if (ctx?.isElementAncestor) { - return ctx; - } - // Start ignoring everything if we enter an element prop - if (parent?.props.element === node) { - return { ...ctx, isElementAncestor: true }; - } - const parentChildren = ctx?.obj?.children ?? acc.objects; if (pathProp !== undefined) {