frontend-app-api: initial routing system implementation
Co-authored-by: Fredrik Adelöw <freben@gmail.com> Co-authored-by: Philipp Hugenroth <philipph@spotify.com> Co-authored-by: Johan Haals <johan.haals@gmail.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/plugin-graphiql": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
|
||||
@@ -30,6 +30,8 @@ import {
|
||||
mergeExtensionParameters,
|
||||
readAppExtensionParameters,
|
||||
} from './wiring/parameters';
|
||||
import { RoutingProvider } from './routing/RoutingContext';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
/** @public */
|
||||
export function createApp(options: { plugins: BackstagePlugin[] }): {
|
||||
@@ -109,6 +111,8 @@ export function createApp(options: { plugins: BackstagePlugin[] }): {
|
||||
createInstance(instanceParams),
|
||||
);
|
||||
|
||||
const routePaths = extractRouteInfoFromInstanceTree(rootInstances);
|
||||
|
||||
return {
|
||||
createRoot() {
|
||||
const rootComponents = rootInstances.map(
|
||||
@@ -118,12 +122,42 @@ export function createApp(options: { plugins: BackstagePlugin[] }): {
|
||||
) as typeof coreExtensionData.reactComponent.T,
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<RoutingProvider routePaths={routePaths}>
|
||||
{rootComponents.map((Component, i) => (
|
||||
<Component key={i} />
|
||||
))}
|
||||
</>
|
||||
</RoutingProvider>
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function extractRouteInfoFromInstanceTree(
|
||||
roots: ExtensionInstance[],
|
||||
): Map<RouteRef, string> {
|
||||
const results = new Map<RouteRef, string>();
|
||||
|
||||
function visit(current: ExtensionInstance, basePath: string) {
|
||||
const routePath = current.data.get(coreExtensionData.routePath.id) ?? '';
|
||||
const routeRef = current.data.get(
|
||||
coreExtensionData.routeRef.id,
|
||||
) as RouteRef;
|
||||
|
||||
// TODO: join paths in a more robust way
|
||||
const fullPath = basePath + routePath;
|
||||
if (routeRef) {
|
||||
results.set(routeRef, fullPath);
|
||||
}
|
||||
|
||||
for (const children of current.attachments.values()) {
|
||||
for (const child of children) {
|
||||
visit(child, fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const root of roots) {
|
||||
visit(root, '');
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ export const RouteExtension = createExtension({
|
||||
routes: {
|
||||
extensionData: {
|
||||
path: coreExtensionData.routePath,
|
||||
ref: coreExtensionData.routeRef,
|
||||
component: coreExtensionData.reactComponent,
|
||||
},
|
||||
},
|
||||
|
||||
+29
-2
@@ -13,13 +13,40 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
import { createContext } from 'react';
|
||||
import React, { createContext, ReactNode } from 'react';
|
||||
|
||||
export interface RoutingContextType {
|
||||
resolve(routeRef: RouteRef, options: { pathname: string }): () => string;
|
||||
resolve(
|
||||
routeRef: RouteRef,
|
||||
options: { pathname: string },
|
||||
): (() => string) | undefined;
|
||||
}
|
||||
|
||||
export const RoutingContext = createContext<RoutingContextType>({
|
||||
resolve: () => () => '',
|
||||
});
|
||||
|
||||
export class RouteResolver {
|
||||
constructor(private readonly routePaths: Map<RouteRef, string>) {}
|
||||
|
||||
resolve(anyRouteRef: RouteRef<{}>): (() => string) | undefined {
|
||||
const basePath = this.routePaths.get(anyRouteRef);
|
||||
if (!basePath) {
|
||||
return undefined;
|
||||
}
|
||||
return () => basePath;
|
||||
}
|
||||
}
|
||||
|
||||
export function RoutingProvider(props: {
|
||||
routePaths: Map<RouteRef, string>;
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<RoutingContext.Provider value={new RouteResolver(props.routePaths)}>
|
||||
{props.children}
|
||||
</RoutingContext.Provider>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user