From e5a0a99980f798c3ac99552efdef98e10b5ef95c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 11 Aug 2025 11:53:07 +0200 Subject: [PATCH] frontend-defaults: rename to loadingElement and switch to Progress component Signed-off-by: Patrik Oldsberg --- .changeset/tender-olives-clean.md | 5 +++++ packages/frontend-defaults/package.json | 1 + packages/frontend-defaults/report.api.md | 2 +- .../frontend-defaults/src/createApp.test.tsx | 16 ++++++++-------- packages/frontend-defaults/src/createApp.tsx | 17 +++++++++-------- yarn.lock | 1 + 6 files changed, 25 insertions(+), 17 deletions(-) create mode 100644 .changeset/tender-olives-clean.md diff --git a/.changeset/tender-olives-clean.md b/.changeset/tender-olives-clean.md new file mode 100644 index 0000000000..1923b6bdc0 --- /dev/null +++ b/.changeset/tender-olives-clean.md @@ -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 `` 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. diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index 485f0f3497..5d4fe573cc 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -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:^", diff --git a/packages/frontend-defaults/report.api.md b/packages/frontend-defaults/report.api.md index 8042345d35..6fd2536251 100644 --- a/packages/frontend-defaults/report.api.md +++ b/packages/frontend-defaults/report.api.md @@ -28,7 +28,7 @@ export interface CreateAppOptions { extensionFactoryMiddleware?: | ExtensionFactoryMiddleware | ExtensionFactoryMiddleware[]; - loadingComponent?: ReactNode; + loadingElement?: ReactNode; pluginInfoResolver?: FrontendPluginInfoResolver; }; bindRoutes?(context: { bind: CreateAppRouteBinder }): void; diff --git a/packages/frontend-defaults/src/createApp.test.tsx b/packages/frontend-defaults/src/createApp.test.tsx index 4200cf92b8..7faef3a8a8 100644 --- a/packages/frontend-defaults/src/createApp.test.tsx +++ b/packages/frontend-defaults/src/createApp.test.tsx @@ -427,7 +427,7 @@ describe('createApp', () => { `); }); - it('should use "Loading..." as the default suspense fallback', async () => { + it('should use 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: "Custom loading message", + loadingElement: Custom loading message, }, }); 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 () => { diff --git a/packages/frontend-defaults/src/createApp.tsx b/packages/frontend-defaults/src/createApp.tsx index 828fbd4622..2c8a80791f 100644 --- a/packages/frontend-defaults/src/createApp.tsx +++ b/packages/frontend-defaults/src/createApp.tsx @@ -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 `` 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 = ; } 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 ( diff --git a/yarn.lock b/yarn.lock index b15db89924..c3b14a7921 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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:^"