feat: refactor to error display instead

Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
benjdlambert
2025-08-07 10:54:10 +02:00
parent 4b3ba2b048
commit 75ea6f14c5
12 changed files with 31 additions and 34 deletions
@@ -27,7 +27,7 @@ import {
iconsApiRef,
useApi,
routeResolutionApiRef,
ErrorBoundary,
ErrorDisplay,
NotFoundErrorPage,
Progress,
} from '@backstage/frontend-plugin-api';
@@ -102,7 +102,7 @@ function LegacyAppContextProvider(props: { children: ReactNode }) {
const ErrorBoundaryFallbackWrapper: AppComponents['ErrorBoundaryFallback'] =
({ plugin, ...rest }) => (
<ErrorBoundary {...rest} plugin={plugin && toNewPlugin(plugin)} />
<ErrorDisplay {...rest} plugin={plugin && toNewPlugin(plugin)} />
);
return {
@@ -24,7 +24,7 @@ import {
AnyRouteRefParams,
SwappableComponentRef,
SwappableComponentsApi,
CoreErrorBoundaryFallbackProps,
CoreErrorDisplayProps,
CoreNotFoundErrorPageProps,
CoreProgressProps,
ExternalRouteRef,
@@ -39,7 +39,7 @@ import {
routeResolutionApiRef,
Progress,
NotFoundErrorPage,
ErrorBoundary,
ErrorDisplay,
} from '@backstage/frontend-plugin-api';
import { ComponentType, useMemo } from 'react';
import { ReactNode } from 'react';
@@ -54,11 +54,11 @@ import { convertLegacyRouteRef } from '../convertLegacyRouteRef';
class CompatComponentsApi implements SwappableComponentsApi {
readonly #Progress: ComponentType<CoreProgressProps>;
readonly #NotFoundErrorPage: ComponentType<CoreNotFoundErrorPageProps>;
readonly #ErrorBoundaryFallback: ComponentType<CoreErrorBoundaryFallbackProps>;
readonly #ErrorBoundaryFallback: ComponentType<CoreErrorDisplayProps>;
constructor(app: AppContext) {
const components = app.getComponents();
const ErrorBoundaryFallback = (props: CoreErrorBoundaryFallbackProps) => (
const ErrorBoundaryFallback = (props: CoreErrorDisplayProps) => (
<components.ErrorBoundaryFallback
{...props}
plugin={props.plugin && toLegacyPlugin(props.plugin)}
@@ -87,7 +87,7 @@ class CompatComponentsApi implements SwappableComponentsApi {
return (() => this.#NotFoundErrorPage) as () => (
props: object,
) => JSX.Element | null;
case ErrorBoundary.ref.id:
case ErrorDisplay.ref.id:
return (() => this.#ErrorBoundaryFallback) as () => (
props: object,
) => JSX.Element | null;
@@ -23,7 +23,7 @@ import {
createRouteRef as createNewRouteRef,
useApi,
NotFoundErrorPage,
ErrorBoundary,
ErrorDisplay,
Progress,
} from '@backstage/frontend-plugin-api';
import {
@@ -102,7 +102,7 @@ describe('ForwardsCompatProvider', () => {
const defaultComponentRefs = {
progress: Progress.ref,
notFoundErrorPage: NotFoundErrorPage.ref,
errorBoundary: ErrorBoundary.ref,
errorDisplay: ErrorDisplay.ref,
};
function Component() {
@@ -126,7 +126,7 @@ describe('ForwardsCompatProvider', () => {
await renderInOldTestApp(compatWrapper(<Component />));
expect(screen.getByTestId('ctx').textContent).toMatchInlineSnapshot(`
"components: progress=true, notFoundErrorPage=true, errorBoundary=true
"components: progress=true, notFoundErrorPage=true, errorDisplay=true
icons: kind:api, kind:component, kind:domain, kind:group, kind:location, kind:system, kind:user, kind:resource, kind:template, brokenImage, catalog, scaffolder, techdocs, search, chat, dashboard, docs, email, github, group, help, user, warning, star, unstarred"
`);
});
@@ -18,7 +18,7 @@ import { ComponentType } from 'react';
import {
SwappableComponentBlueprint,
ApiBlueprint,
CoreErrorBoundaryFallbackProps,
CoreErrorDisplayProps,
createExtension,
createFrontendModule,
ExtensionDefinition,
@@ -27,7 +27,7 @@ import {
RouterBlueprint,
SignInPageBlueprint,
ThemeBlueprint,
ErrorBoundary as SwappableErrorBoundary,
ErrorDisplay as SwappableErrorDisplay,
NotFoundErrorPage as SwappableNotFoundErrorPage,
Progress as SwappableProgress,
} from '@backstage/frontend-plugin-api';
@@ -179,9 +179,7 @@ export function convertLegacyAppOptions(
}
if (ErrorBoundaryFallback) {
const WrappedErrorBoundaryFallback = (
props: CoreErrorBoundaryFallbackProps,
) =>
const WrappedErrorBoundaryFallback = (props: CoreErrorDisplayProps) =>
compatWrapper(
<ErrorBoundaryFallback
{...props}
@@ -193,7 +191,7 @@ export function convertLegacyAppOptions(
SwappableComponentBlueprint.make({
params: define =>
define({
component: SwappableErrorBoundary,
component: SwappableErrorDisplay,
loader: () =>
componentCompatWrapper(WrappedErrorBoundaryFallback),
}),
@@ -40,5 +40,5 @@ export interface SwappableComponentsApi {
* @public
*/
export const swappableComponentsApiRef = createApiRef<SwappableComponentsApi>({
id: 'core.swappableComponents',
id: 'core.swappable-components',
});
@@ -15,7 +15,7 @@
*/
import {
CoreErrorBoundaryFallbackProps,
CoreErrorDisplayProps,
CoreNotFoundErrorPageProps,
CoreProgressProps,
} from '../types';
@@ -39,7 +39,6 @@ export const NotFoundErrorPage =
/**
* @public
*/
export const ErrorBoundary =
createSwappableComponent<CoreErrorBoundaryFallbackProps>({
id: 'core.components.errorBoundary',
});
export const ErrorDisplay = createSwappableComponent<CoreErrorDisplayProps>({
id: 'core.components.errorDisplay',
});
@@ -14,13 +14,12 @@
* limitations under the License.
*/
import { Component, ComponentType, PropsWithChildren } from 'react';
import { Component, PropsWithChildren } from 'react';
import { FrontendPlugin } from '../wiring';
import { CoreErrorBoundaryFallbackProps } from '../types';
import { ErrorDisplay } from './DefaultSwappableComponents';
type ErrorBoundaryProps = PropsWithChildren<{
plugin?: FrontendPlugin;
Fallback: ComponentType<CoreErrorBoundaryFallbackProps>;
}>;
type ErrorBoundaryState = { error?: Error };
@@ -41,13 +40,15 @@ export class ErrorBoundary extends Component<
render() {
const { error } = this.state;
const { plugin, children, Fallback } = this.props;
const { plugin, children } = this.props;
if (error) {
return (
<Fallback
<ErrorDisplay
// todo: do we want to just use useAppNode hook in the ErrorDisplay instead?
plugin={plugin}
error={error}
// todo: probably change this to onResetError
resetError={this.handleErrorReset}
/>
);
@@ -29,7 +29,6 @@ import { AppNode } from '../apis';
import { Progress } from '@backstage/core-components';
import { coreExtensionData } from '../wiring';
import { AppNodeProvider } from './AppNodeProvider';
import { ErrorBoundary as ErrorBoundaryComponent } from './DefaultSwappableComponents';
type RouteTrackerProps = PropsWithChildren<{
enabled?: boolean;
@@ -77,7 +76,7 @@ export function ExtensionBoundary(props: ExtensionBoundaryProps) {
return (
<AppNodeProvider node={node}>
<Suspense fallback={<Progress />}>
<ErrorBoundary plugin={plugin} Fallback={ErrorBoundaryComponent}>
<ErrorBoundary plugin={plugin}>
<AnalyticsContext attributes={attributes}>
<RouteTracker enabled={hasRoutePathOutput}>{children}</RouteTracker>
</AnalyticsContext>
@@ -21,7 +21,7 @@ describe('createSwappableComponent', () => {
it('can be created and read', () => {
const { ref } = createSwappableComponent({ id: 'foo' });
expect(ref.id).toBe('foo');
expect(String(ref)).toBe('ComponentRef{id=foo}');
expect(String(ref)).toBe('SwappableComponentRef{id=foo}');
});
it('should allow defining a default component implementation', () => {
+1 -1
View File
@@ -34,5 +34,5 @@ export * from './wiring';
export type {
CoreProgressProps,
CoreNotFoundErrorPageProps,
CoreErrorBoundaryFallbackProps,
CoreErrorDisplayProps,
} from './types';
+1 -1
View File
@@ -26,7 +26,7 @@ export type CoreNotFoundErrorPageProps = {
};
/** @public */
export type CoreErrorBoundaryFallbackProps = {
export type CoreErrorDisplayProps = {
plugin?: FrontendPlugin;
error: Error;
resetError: () => void;
+2 -2
View File
@@ -16,7 +16,7 @@
import {
NotFoundErrorPage as SwappableNotFoundErrorPage,
Progress as SwappableProgress,
ErrorBoundary as SwappableErrorBoundary,
ErrorDisplay as SwappableErrorDisplay,
SwappableComponentBlueprint,
} from '@backstage/frontend-plugin-api';
@@ -50,7 +50,7 @@ export const ErrorBoundary = SwappableComponentBlueprint.make({
name: 'core.components.errorBoundary',
params: define =>
define({
component: SwappableErrorBoundary,
component: SwappableErrorDisplay,
loader: () => props => {
const { plugin, error, resetError } = props;
const title = `Error in ${plugin?.id}`;