From 62e58de887170999728e028d700bb6ca0cf9e255 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 8 Dec 2022 00:04:15 +0100 Subject: [PATCH] core-app-api: added app.createRoot() Signed-off-by: Patrik Oldsberg --- .changeset/fuzzy-rivers-search.md | 47 +++++++++++++++++++- packages/core-app-api/api-report.md | 1 + packages/core-app-api/src/app/AppManager.tsx | 16 +++++++ packages/core-app-api/src/app/types.ts | 27 +++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/.changeset/fuzzy-rivers-search.md b/.changeset/fuzzy-rivers-search.md index 06e2dda6d7..4a82d1884c 100644 --- a/.changeset/fuzzy-rivers-search.md +++ b/.changeset/fuzzy-rivers-search.md @@ -2,4 +2,49 @@ '@backstage/core-app-api': minor --- -Added a new `AppRouter` component that replaces the same component currently created through `app.getRouter()`. +Added a new `AppRouter` component and `app.createRoot()` method that replaces `app.getRouter()` and `app.getProvider()`, which are now deprecated. The new `AppRouter` component is a drop-in replacement for the old router component, while the new `app.createRoot()` method is used instead of the old provider component. + +An old app setup might look like this: + +```tsx +const app = createApp(/* ... */); + +const AppProvider = app.getProvider(); +const AppRouter = app.getRouter(); + +const routes = ...; + +const App = () => ( + + + + + {routes} + + +); + +export default App; +``` + +With these new APIs, the setup now looks like this: + +```tsx +import { AppRouter } from '@backstage/core-app-api'; + +const app = createApp(/* ... */); + +const routes = ...; + +export default app.createRoot( + <> + + + + {routes} + + , +); +``` + +Note that `app.createRoot()` accepts a React element, rather than a component. diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 137d6d72ca..c07ea55b67 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -262,6 +262,7 @@ export type AuthApiCreateOptions = { export type BackstageApp = { getPlugins(): BackstagePlugin[]; getSystemIcon(key: string): IconComponent | undefined; + createRoot(element: JSX.Element): ComponentType<{}>; getProvider(): ComponentType<{}>; getRouter(): ComponentType<{}>; }; diff --git a/packages/core-app-api/src/app/AppManager.tsx b/packages/core-app-api/src/app/AppManager.tsx index a5c981a54e..5a752ad5df 100644 --- a/packages/core-app-api/src/app/AppManager.tsx +++ b/packages/core-app-api/src/app/AppManager.tsx @@ -248,7 +248,23 @@ export class AppManager implements BackstageApp { return this.components; } + createRoot(element: JSX.Element): ComponentType<{}> { + const AppProvider = this.getProvider(); + const AppRoot = () => { + return {element}; + }; + return AppRoot; + } + + #getProviderCalled = false; getProvider(): ComponentType<{}> { + if (this.#getProviderCalled) { + throw new Error( + 'app.getProvider() or app.createRoot() has already been called, and can only be called once', + ); + } + this.#getProviderCalled = true; + const appContext = new AppContextImpl(this); // We only validate routes once diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 861766f8d3..82c05ce268 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -298,9 +298,36 @@ export type BackstageApp = { */ getSystemIcon(key: string): IconComponent | undefined; + /** + * Creates the root component that renders the entire app. + * + * @remarks + * + * This method must only be called once, and you have to provide it the entire + * app element tree. The element tree will be analyzed to discover plugins, + * routes, and other app features. The returned component will render all + * of the app elements wrapped within the app context provider. + * + * @example + * ```tsx + * export default app.createRoot( + * <> + * + * + * + * {routes} + * + * , + * ); + * ``` + */ + createRoot(element: JSX.Element): ComponentType<{}>; + /** * Provider component that should wrap the Router created with getRouter() * and any other components that need to be within the app context. + * + * @deprecated Use {@link BackstageApp.createRoot} instead. */ getProvider(): ComponentType<{}>;