core-app-api: remedy wrong relative route reference resolution
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-app-api': patch
|
||||
---
|
||||
|
||||
Fixed a bug where `useRouteRef` would fail in situations where relative navigation was needed and the app was is mounted on a sub-path. This would typically show up as a failure to navigate to a tab on an entity page.
|
||||
@@ -100,23 +100,55 @@ describe('RouteResolver', () => {
|
||||
expect(r.resolve(externalRef4, '/')?.({ x: '6x' })).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should resolve an absolute route and an app base path', () => {
|
||||
it('should resolve an absolute route and sub route with an app base path', () => {
|
||||
const r = new RouteResolver(
|
||||
new Map([[ref1, '/my-route']]),
|
||||
new Map(),
|
||||
[{ routeRefs: new Set([ref1]), path: '/my-route', ...rest }],
|
||||
new Map<RouteRef, string>([
|
||||
[ref2, '/my-parent/:x'],
|
||||
[ref1, '/my-route'],
|
||||
]),
|
||||
new Map<RouteRef, RouteRef>([[ref1, ref2]]),
|
||||
[
|
||||
{
|
||||
routeRefs: new Set([ref2]),
|
||||
path: '/my-parent/:x',
|
||||
...rest,
|
||||
children: [
|
||||
MATCH_ALL_ROUTE,
|
||||
{ routeRefs: new Set([ref1]), path: '/my-route', ...rest },
|
||||
],
|
||||
},
|
||||
],
|
||||
new Map(),
|
||||
'/base',
|
||||
);
|
||||
|
||||
expect(r.resolve(ref1, '/')?.()).toBe('/base/my-route');
|
||||
expect(r.resolve(ref2, '/')?.({ x: '1x' })).toBe(undefined);
|
||||
expect(r.resolve(subRef1, '/')?.()).toBe('/base/my-route/foo');
|
||||
expect(r.resolve(subRef2, '/')?.({ a: '2a' })).toBe(
|
||||
'/base/my-route/foo/2a',
|
||||
expect(r.resolve(ref1, '/my-parent/1x')?.()).toBe(
|
||||
'/base/my-parent/1x/my-route',
|
||||
);
|
||||
expect(r.resolve(ref1, '/base/my-parent/1x')?.()).toBe(
|
||||
'/base/my-parent/1x/my-route',
|
||||
);
|
||||
expect(r.resolve(ref2, '/')?.({ x: '1x' })).toBe('/base/my-parent/1x');
|
||||
expect(r.resolve(ref2, '/base')?.({ x: '1x' })).toBe('/base/my-parent/1x');
|
||||
expect(r.resolve(ref3, '/')?.({ y: '1y' })).toBe(undefined);
|
||||
expect(r.resolve(subRef1, '/my-parent/2x')?.()).toBe(
|
||||
'/base/my-parent/2x/my-route/foo',
|
||||
);
|
||||
expect(r.resolve(subRef1, '/base/my-parent/2x')?.()).toBe(
|
||||
'/base/my-parent/2x/my-route/foo',
|
||||
);
|
||||
expect(r.resolve(subRef2, '/my-parent/3x')?.({ a: '2a' })).toBe(
|
||||
'/base/my-parent/3x/my-route/foo/2a',
|
||||
);
|
||||
expect(r.resolve(subRef2, '/base/my-parent/3x')?.({ a: '2a' })).toBe(
|
||||
'/base/my-parent/3x/my-route/foo/2a',
|
||||
);
|
||||
expect(r.resolve(subRef3, '/')?.({ x: '5x' })).toBe(
|
||||
'/base/my-parent/5x/bar',
|
||||
);
|
||||
expect(r.resolve(subRef4, '/')?.({ x: '6x', a: '4a' })).toBe(
|
||||
'/base/my-parent/6x/bar/4a',
|
||||
);
|
||||
expect(r.resolve(subRef3, '/')?.({ x: '3x' })).toBe(undefined);
|
||||
expect(r.resolve(subRef4, '/')?.({ x: '4x', a: '4a' })).toBe(undefined);
|
||||
expect(r.resolve(externalRef1, '/')?.()).toBe(undefined);
|
||||
expect(r.resolve(externalRef2, '/')?.()).toBe(undefined);
|
||||
expect(r.resolve(externalRef3, '/')?.({ x: '5x' })).toBe(undefined);
|
||||
|
||||
@@ -207,6 +207,20 @@ export class RouteResolver {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// The location that we get passed in uses the full path, so start by trimming off
|
||||
// the app base path prefix in case we're running the app on a sub-path.
|
||||
let relativeSourceLocation: Parameters<typeof matchRoutes>[1];
|
||||
if (typeof sourceLocation === 'string') {
|
||||
relativeSourceLocation = this.trimPath(sourceLocation);
|
||||
} else if (sourceLocation.pathname) {
|
||||
relativeSourceLocation = {
|
||||
...sourceLocation,
|
||||
pathname: this.trimPath(sourceLocation.pathname),
|
||||
};
|
||||
} else {
|
||||
relativeSourceLocation = sourceLocation;
|
||||
}
|
||||
|
||||
// Next we figure out the base path, which is the combination of the common parent path
|
||||
// between our current location and our target location, as well as the additional path
|
||||
// that is the difference between the parent path and the base of our target location.
|
||||
@@ -214,7 +228,7 @@ export class RouteResolver {
|
||||
this.appBasePath +
|
||||
resolveBasePath(
|
||||
targetRef,
|
||||
sourceLocation,
|
||||
relativeSourceLocation,
|
||||
this.routePaths,
|
||||
this.routeParents,
|
||||
this.routeObjects,
|
||||
@@ -225,4 +239,15 @@ export class RouteResolver {
|
||||
};
|
||||
return routeFunc;
|
||||
}
|
||||
|
||||
private trimPath(targetPath: string) {
|
||||
if (!targetPath) {
|
||||
return targetPath;
|
||||
}
|
||||
|
||||
if (targetPath.startsWith(this.appBasePath)) {
|
||||
return targetPath.slice(this.appBasePath.length);
|
||||
}
|
||||
return targetPath;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user