From c26c40956605409fd72ed63cd26ee32e8971b973 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 27 Oct 2023 08:47:24 +0200 Subject: [PATCH] feat: create core component factories Signed-off-by: Camila Belo --- packages/app-next/src/App.tsx | 38 ++++++++ .../src/extensions/CoreComponents.tsx | 24 +++++ .../src/extensions/CoreRoutes.tsx | 14 ++- .../frontend-app-api/src/wiring/createApp.tsx | 12 ++- .../extensions/createComponentExtension.tsx | 95 +++++++++++++++++++ .../src/extensions/index.ts | 6 ++ 6 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index 67faf8d646..88dc4a7207 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -29,6 +29,7 @@ import { createExtension, createApiExtension, createExtensionOverrides, + createNotFoundErrorPageExtension, } from '@backstage/frontend-plugin-api'; import techdocsPlugin from '@backstage/plugin-techdocs/alpha'; import { homePage } from './HomePage'; @@ -48,6 +49,8 @@ import { } from '@backstage/integration-react'; import { createSignInPageExtension } from '@backstage/frontend-plugin-api'; import { SignInPage } from '@backstage/core-components'; +import { Box, Typography } from '@material-ui/core'; +import { Button } from '@backstage/core-components'; /* @@ -108,6 +111,40 @@ const scmIntegrationApi = createApiExtension({ }), }); +const customNotFoundErrorPage = createNotFoundErrorPageExtension({ + component: () => ( + + 404 + + Bowie was unable to locate this page. Please contact your support team + if this page used to exist. + + Backstage bowie + + + ), +}); + const collectedLegacyPlugins = collectLegacyRoutes( } /> @@ -129,6 +166,7 @@ const app = createApp({ scmAuthExtension, scmIntegrationApi, signInPage, + customNotFoundErrorPage, ], }), ], diff --git a/packages/frontend-app-api/src/extensions/CoreComponents.tsx b/packages/frontend-app-api/src/extensions/CoreComponents.tsx index 3e1bc8e8bc..a0651d6423 100644 --- a/packages/frontend-app-api/src/extensions/CoreComponents.tsx +++ b/packages/frontend-app-api/src/extensions/CoreComponents.tsx @@ -19,12 +19,36 @@ import { createExtension, coreExtensionData, createExtensionInput, + createProgressExtension, + createBootErrorPageExtension, + createNotFoundErrorPageExtension, + createErrorBoundaryFallbackExtension, } from '@backstage/frontend-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { components as defaultComponents } from '../../../app-defaults/src/defaults'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports import { AppContextProvider } from '../../../core-app-api/src/app/AppContext'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { useApp } from '../../../core-plugin-api/src/app/useApp'; +export const DefaultProgressComponent = createProgressExtension({ + component: defaultComponents.Progress, +}); + +export const DefaultBootErrorPageComponent = createBootErrorPageExtension({ + component: defaultComponents.BootErrorPage, +}); + +export const DefaultNotFoundErrorPageComponent = + createNotFoundErrorPageExtension({ + component: defaultComponents.NotFoundErrorPage, + }); + +export const DefaultErrorBoundaryComponent = + createErrorBoundaryFallbackExtension({ + component: defaultComponents.ErrorBoundaryFallback, + }); + export const CoreComponents = createExtension({ id: 'core.components', attachTo: { id: 'core', input: 'components' }, diff --git a/packages/frontend-app-api/src/extensions/CoreRoutes.tsx b/packages/frontend-app-api/src/extensions/CoreRoutes.tsx index 985fd07be9..e2798c091d 100644 --- a/packages/frontend-app-api/src/extensions/CoreRoutes.tsx +++ b/packages/frontend-app-api/src/extensions/CoreRoutes.tsx @@ -21,6 +21,8 @@ import { createExtensionInput, } from '@backstage/frontend-plugin-api'; import { useRoutes } from 'react-router-dom'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { useApp } from '../../../core-plugin-api/src/app/useApp'; export const CoreRoutes = createExtension({ id: 'core.routes', @@ -37,12 +39,18 @@ export const CoreRoutes = createExtension({ }, factory({ inputs }) { const Routes = () => { - const element = useRoutes( - inputs.routes.map(route => ({ + const app = useApp(); + const { NotFoundErrorPage } = app.getComponents(); + const element = useRoutes([ + ...inputs.routes.map(route => ({ path: `${route.path}/*`, element: route.element, })), - ); + { + path: '*', + element: , + }, + ]); return element; }; diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 8d51df84c0..caa7064b2d 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -89,7 +89,13 @@ import { RoutingProvider } from '../routing/RoutingProvider'; import { resolveRouteBindings } from '../routing/resolveRouteBindings'; import { collectRouteIds } from '../routing/collectRouteIds'; import { createAppTree } from '../tree'; -import { CoreComponents } from '../extensions/CoreComponents'; +import { + CoreComponents, + DefaultProgressComponent, + DefaultErrorBoundaryComponent, + DefaultBootErrorPageComponent, + DefaultNotFoundErrorPageComponent, +} from '../extensions/CoreComponents'; import { AppNode } from '@backstage/frontend-plugin-api'; import { toLegacyPlugin } from '../routing/toLegacyPlugin'; import { InternalAppContext } from './InternalAppContext'; @@ -106,6 +112,10 @@ const builtinExtensions = [ CoreNav, CoreLayout, CoreComponents, + DefaultProgressComponent, + DefaultErrorBoundaryComponent, + DefaultBootErrorPageComponent, + DefaultNotFoundErrorPageComponent, LightTheme, DarkTheme, ]; diff --git a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx new file mode 100644 index 0000000000..5c882dd638 --- /dev/null +++ b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx @@ -0,0 +1,95 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { AppComponents } from '../../../core-app-api/src/app/types'; +import { coreExtensionData, createExtension } from '../wiring'; + +/** @public */ +export function createBootErrorPageExtension(options: { + component: AppComponents['BootErrorPage']; +}) { + return createExtension({ + id: `core.components.bootErrorPage`, + attachTo: { id: 'core.components', input: 'bootErrorPage' }, + inputs: {}, + output: { + component: coreExtensionData.components.bootErrorPage, + }, + factory({ bind }) { + bind({ + component: options.component, + }); + }, + }); +} + +/** @public */ +export function createNotFoundErrorPageExtension(options: { + component: AppComponents['NotFoundErrorPage']; +}) { + return createExtension({ + id: `core.components.notFoundErrorPage`, + attachTo: { id: 'core.components', input: 'notFoundErrorPage' }, + inputs: {}, + output: { + component: coreExtensionData.components.notFoundErrorPage, + }, + factory({ bind }) { + bind({ + component: options.component, + }); + }, + }); +} + +/** @public */ +export function createErrorBoundaryFallbackExtension(options: { + component: AppComponents['ErrorBoundaryFallback']; +}) { + return createExtension({ + id: `core.components.errorBoundaryFallback`, + attachTo: { id: 'core.components', input: 'errorBoundaryFallback' }, + inputs: {}, + output: { + component: coreExtensionData.components.errorBoundaryFallback, + }, + factory({ bind }) { + bind({ + component: options.component, + }); + }, + }); +} + +/** @public */ +export function createProgressExtension(options: { + component: AppComponents['Progress']; +}) { + return createExtension({ + id: `core.components.progress`, + attachTo: { id: 'core.components', input: 'progress' }, + inputs: {}, + output: { + component: coreExtensionData.components.progress, + }, + factory({ bind }) { + bind({ + component: options.component, + }); + }, + }); +} diff --git a/packages/frontend-plugin-api/src/extensions/index.ts b/packages/frontend-plugin-api/src/extensions/index.ts index d696774272..d036cb5dda 100644 --- a/packages/frontend-plugin-api/src/extensions/index.ts +++ b/packages/frontend-plugin-api/src/extensions/index.ts @@ -19,3 +19,9 @@ export { createPageExtension } from './createPageExtension'; export { createNavItemExtension } from './createNavItemExtension'; export { createSignInPageExtension } from './createSignInPageExtension'; export { createThemeExtension } from './createThemeExtension'; +export { + createProgressExtension, + createBootErrorPageExtension, + createNotFoundErrorPageExtension, + createErrorBoundaryFallbackExtension, +} from './createComponentExtension';