Add loadingComponent parameter to createApp()
This allows Backstage instances to show their own "fallback" content when the app is loading, such as to avoid the "flicker of white" on-reload. Signed-off-by: Mitchell Hentges <mhentges@spotify.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': minor
|
||||
---
|
||||
|
||||
Add `loadingComponent` parameter to `createApp()`
|
||||
@@ -8,6 +8,7 @@ import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { FrontendFeature } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent } from '@backstage/core-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { SubRouteRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -21,6 +22,7 @@ export function createApp(options?: {
|
||||
config: ConfigApi;
|
||||
}>;
|
||||
bindRoutes?(context: { bind: CreateAppRouteBinder }): void;
|
||||
loadingComponent?: ReactNode;
|
||||
}): {
|
||||
createRoot(): JSX_2.Element;
|
||||
};
|
||||
|
||||
@@ -297,4 +297,36 @@ describe('createApp', () => {
|
||||
</app>"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should use "Loading..." as the default suspense fallback', async () => {
|
||||
const app = createApp({
|
||||
configLoader: () => new Promise(() => {}),
|
||||
});
|
||||
|
||||
await renderWithEffects(app.createRoot());
|
||||
|
||||
await expect(screen.findByText('Loading...')).resolves.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should use no suspense fallback if the "loadingComponent" is null', async () => {
|
||||
const app = createApp({
|
||||
configLoader: () => new Promise(() => {}),
|
||||
loadingComponent: null,
|
||||
});
|
||||
|
||||
await renderWithEffects(app.createRoot());
|
||||
|
||||
expect(screen.queryByText('Loading...')).toBeNull();
|
||||
});
|
||||
|
||||
it('should use a custom "loadingComponent"', async () => {
|
||||
const app = createApp({
|
||||
configLoader: () => new Promise(() => {}),
|
||||
loadingComponent: <span>"Custom loading message"</span>,
|
||||
});
|
||||
|
||||
await renderWithEffects(app.createRoot());
|
||||
|
||||
expect(screen.queryByText('Custom loading message')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { JSX } from 'react';
|
||||
import React, { JSX, ReactNode } from 'react';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
AppTree,
|
||||
@@ -170,9 +170,21 @@ export function createApp(options?: {
|
||||
features?: (FrontendFeature | CreateAppFeatureLoader)[];
|
||||
configLoader?: () => Promise<{ config: ConfigApi }>;
|
||||
bindRoutes?(context: { bind: CreateAppRouteBinder }): void;
|
||||
/**
|
||||
* The component to render while loading the app (waiting for config, features, etc)
|
||||
*
|
||||
* Is the text "Loading..." by default.
|
||||
* If set to "null" then no loading fallback component is rendered. *
|
||||
*/
|
||||
loadingComponent?: ReactNode;
|
||||
}): {
|
||||
createRoot(): JSX.Element;
|
||||
} {
|
||||
let suspenseFallback = options?.loadingComponent;
|
||||
if (suspenseFallback === undefined) {
|
||||
suspenseFallback = 'Loading...';
|
||||
}
|
||||
|
||||
async function appLoader() {
|
||||
const config =
|
||||
(await options?.configLoader?.().then(c => c.config)) ??
|
||||
@@ -214,7 +226,7 @@ export function createApp(options?: {
|
||||
createRoot() {
|
||||
const LazyApp = React.lazy(appLoader);
|
||||
return (
|
||||
<React.Suspense fallback="Loading...">
|
||||
<React.Suspense fallback={suspenseFallback}>
|
||||
<LazyApp />
|
||||
</React.Suspense>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user