frontend-defaults: rename to loadingElement and switch to Progress component

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-08-11 11:53:07 +02:00
parent 0aa2fc14ab
commit e5a0a99980
6 changed files with 25 additions and 17 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-defaults': patch
---
**BREAKING**: The `loadingComponent` option has been renamed to `loadingElement`, which is now found under `advanced.loadingElement`. The default loading element has also been switched to `<Progress />` from `@backstage/core-components`. This is of course an improvement over the previous `"Loading..."` text, but also helps prevent flicker when the app loading is fast.
+1
View File
@@ -32,6 +32,7 @@
},
"dependencies": {
"@backstage/config": "workspace:^",
"@backstage/core-components": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/frontend-app-api": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
+1 -1
View File
@@ -28,7 +28,7 @@ export interface CreateAppOptions {
extensionFactoryMiddleware?:
| ExtensionFactoryMiddleware
| ExtensionFactoryMiddleware[];
loadingComponent?: ReactNode;
loadingElement?: ReactNode;
pluginInfoResolver?: FrontendPluginInfoResolver;
};
bindRoutes?(context: { bind: CreateAppRouteBinder }): void;
@@ -427,7 +427,7 @@ describe('createApp', () => {
`);
});
it('should use "Loading..." as the default suspense fallback', async () => {
it('should use <Progress /> as the default suspense fallback', async () => {
const app = createApp({
advanced: {
configLoader: () => new Promise(() => {}),
@@ -436,33 +436,33 @@ describe('createApp', () => {
await renderWithEffects(app.createRoot());
await expect(screen.findByText('Loading...')).resolves.toBeInTheDocument();
await expect(screen.findByTestId('progress')).resolves.toBeInTheDocument();
});
it('should use no suspense fallback if the "loadingComponent" is null', async () => {
it('should use no suspense fallback if the loadingElement is null', async () => {
const app = createApp({
advanced: {
configLoader: () => new Promise(() => {}),
loadingComponent: null,
loadingElement: null,
},
});
await renderWithEffects(app.createRoot());
expect(screen.queryByText('Loading...')).toBeNull();
expect(screen.queryByTestId('progress')).toBeNull();
});
it('should use a custom "loadingComponent"', async () => {
it('should use a custom loadingElement', async () => {
const app = createApp({
advanced: {
configLoader: () => new Promise(() => {}),
loadingComponent: <span>"Custom loading message"</span>,
loadingElement: <span>Custom loading message</span>,
},
});
await renderWithEffects(app.createRoot());
expect(screen.queryByText('Custom loading message')).toBeNull();
expect(screen.queryByText('Custom loading message')).toBeInTheDocument();
});
it('should allow overriding the app plugin', async () => {
+9 -8
View File
@@ -22,6 +22,7 @@ import {
FrontendFeature,
FrontendFeatureLoader,
} from '@backstage/frontend-plugin-api';
import { Progress } from '@backstage/core-components';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { defaultConfigLoaderSync } from '../../core-app-api/src/app/defaultConfigLoader';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
@@ -87,13 +88,12 @@ export interface CreateAppOptions {
| ExtensionFactoryMiddleware[];
/**
* The component to render while loading the app (waiting for config,
* features, etc).
* The element to render while loading the app (waiting for config, features, etc).
*
* This is the text "Loading..." by default. If set to "null" then no loading
* fallback component is rendered at all.
* This is the `<Progress />` component from `@backstage/core-components` by default.
* If set to `null` then no loading fallback element is rendered at all.
*/
loadingComponent?: ReactNode;
loadingElement?: ReactNode;
/**
* Allows for customizing how plugin info is retrieved.
@@ -110,9 +110,9 @@ export interface CreateAppOptions {
export function createApp(options?: CreateAppOptions): {
createRoot(): JSX.Element;
} {
let suspenseFallback = options?.advanced?.loadingComponent;
let suspenseFallback = options?.advanced?.loadingElement;
if (suspenseFallback === undefined) {
suspenseFallback = 'Loading...';
suspenseFallback = <Progress />;
}
async function appLoader() {
@@ -143,9 +143,10 @@ export function createApp(options?: CreateAppOptions): {
return { default: () => rootEl };
}
const LazyApp = lazy(appLoader);
return {
createRoot() {
const LazyApp = lazy(appLoader);
return (
<Suspense fallback={suspenseFallback}>
<LazyApp />
+1
View File
@@ -3489,6 +3489,7 @@ __metadata:
dependencies:
"@backstage/cli": "workspace:^"
"@backstage/config": "workspace:^"
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/frontend-app-api": "workspace:^"