diff --git a/.changeset/light-eggs-switch.md b/.changeset/light-eggs-switch.md index 3fbb77b0d8..26934f155e 100644 --- a/.changeset/light-eggs-switch.md +++ b/.changeset/light-eggs-switch.md @@ -2,4 +2,4 @@ '@backstage/dev-utils': patch --- -Allow defining custom side bar item for page +Allow defining custom sidebar item for page and login for the development app diff --git a/packages/dev-utils/api-report.md b/packages/dev-utils/api-report.md index d30df29200..0a5b3d10e8 100644 --- a/packages/dev-utils/api-report.md +++ b/packages/dev-utils/api-report.md @@ -13,7 +13,9 @@ import { Entity } from '@backstage/catalog-model'; import { GridProps } from '@material-ui/core/Grid'; import { IconComponent } from '@backstage/core-plugin-api'; import { PropsWithChildren } from 'react'; +import { default as React_2 } from 'react'; import { ReactNode } from 'react'; +import { SignInProviderConfig } from '@backstage/core-components'; // @public export function createDevApp(): DevAppBuilder; @@ -22,6 +24,8 @@ export function createDevApp(): DevAppBuilder; export class DevAppBuilder { addPage(opts: DevAppPageOptions): DevAppBuilder; addRootChild(node: ReactNode): DevAppBuilder; + addSidebarItem(sidebarItem: JSX.Element): DevAppBuilder; + addSignInProvider(provider: SignInProviderConfig): this; addThemes(themes: AppTheme[]): this; build(): ComponentType>; registerApi< @@ -42,7 +46,6 @@ export type DevAppPageOptions = { children?: JSX.Element; title?: string; icon?: IconComponent; - sidebarItem?: JSX.Element; }; // @public (undocumented) @@ -51,4 +54,10 @@ export const EntityGridItem: ( entity: Entity; }, ) => JSX.Element; + +// @public +export const SidebarSignOutButton: (props: { + icon?: IconComponent; + text?: string; +}) => React_2.JSX.Element; ``` diff --git a/packages/dev-utils/src/components/SidebarSignOutButton/SidebarSignOutButton.tsx b/packages/dev-utils/src/components/SidebarSignOutButton/SidebarSignOutButton.tsx new file mode 100644 index 0000000000..9cfe888d4d --- /dev/null +++ b/packages/dev-utils/src/components/SidebarSignOutButton/SidebarSignOutButton.tsx @@ -0,0 +1,44 @@ +/* + * Copyright 2024 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. + */ + +import React from 'react'; +import { SidebarItem } from '@backstage/core-components'; +import LockIcon from '@material-ui/icons/Lock'; +import { + IconComponent, + identityApiRef, + useApi, +} from '@backstage/core-plugin-api'; + +/** + * Button for sidebar that signs out user + * + * @public + */ +export const SidebarSignOutButton = (props: { + icon?: IconComponent; + text?: string; +}) => { + const identityApi = useApi(identityApiRef); + + return ( + identityApi.signOut()} + icon={props.icon ?? LockIcon} + text={props.text ?? 'Sign Out'} + /> + ); +}; diff --git a/packages/dev-utils/src/components/SidebarSignOutButton/index.ts b/packages/dev-utils/src/components/SidebarSignOutButton/index.ts new file mode 100644 index 0000000000..6182e1fa12 --- /dev/null +++ b/packages/dev-utils/src/components/SidebarSignOutButton/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2024 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. + */ +export * from './SidebarSignOutButton'; diff --git a/packages/dev-utils/src/components/index.ts b/packages/dev-utils/src/components/index.ts index d71b424a34..8959b6e7b1 100644 --- a/packages/dev-utils/src/components/index.ts +++ b/packages/dev-utils/src/components/index.ts @@ -15,3 +15,4 @@ */ export * from './EntityGridItem'; +export * from './SidebarSignOutButton'; diff --git a/packages/dev-utils/src/devApp/render.test.tsx b/packages/dev-utils/src/devApp/render.test.tsx index 2da6ef370a..7fc0024cc3 100644 --- a/packages/dev-utils/src/devApp/render.test.tsx +++ b/packages/dev-utils/src/devApp/render.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { createDevApp } from './render'; -import { useApi, configApiRef } from '@backstage/core-plugin-api'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; const anyEnv = (process.env = { ...process.env }) as any; @@ -28,6 +28,7 @@ describe('DevAppBuilder', () => { context: 'test', data: { app: { title: 'Test App' }, + backend: { baseUrl: 'http://localhost' }, }, }, ]; diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index 26fd96052b..3b3c993a6b 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -25,6 +25,8 @@ import { SidebarPage, SidebarSpace, SidebarSpacer, + SignInPage, + SignInProviderConfig, } from '@backstage/core-components'; import { AnyApiFactory, @@ -48,6 +50,7 @@ import React, { ComponentType, PropsWithChildren, ReactNode } from 'react'; import { createRoutesFromChildren, Route } from 'react-router-dom'; import { SidebarThemeSwitcher } from './SidebarThemeSwitcher'; import 'react-dom'; +import { SidebarSignOutButton } from '../components'; let ReactDOMPromise: Promise< typeof import('react-dom') | typeof import('react-dom/client') @@ -80,7 +83,6 @@ export type DevAppPageOptions = { children?: JSX.Element; title?: string; icon?: IconComponent; - sidebarItem?: JSX.Element; }; /** @@ -95,6 +97,7 @@ export class DevAppBuilder { private readonly rootChildren = new Array(); private readonly routes = new Array(); private readonly sidebarItems = new Array(); + private readonly signInProviders = new Array(); private defaultPage?: string; private themes?: Array; @@ -129,6 +132,16 @@ export class DevAppBuilder { return this; } + /** + * Adds a new sidebar item to the dev app. + * + * Useful for adding only sidebar items without a corresponding page. + */ + addSidebarItem(sidebarItem: JSX.Element): DevAppBuilder { + this.sidebarItems.push(sidebarItem); + return this; + } + /** * Adds a page component along with accompanying sidebar item. * @@ -142,9 +155,7 @@ export class DevAppBuilder { this.defaultPage = path; } - if (opts.sidebarItem) { - this.sidebarItems.push(opts.sidebarItem); - } else if (opts.title) { + if (opts.title) { this.sidebarItems.push( { + return ( + + ); + }, + }, bindRoutes: ({ bind }) => { for (const plugin of this.plugins ?? []) { const targets: Record> = {}; @@ -221,6 +252,7 @@ export class DevAppBuilder { + {this.routes} diff --git a/plugins/notifications/dev/index.tsx b/plugins/notifications/dev/index.tsx index 1406f55b59..f4d9176a70 100644 --- a/plugins/notifications/dev/index.tsx +++ b/plugins/notifications/dev/index.tsx @@ -18,12 +18,11 @@ import { createDevApp } from '@backstage/dev-utils'; import { NotificationsPage, notificationsPlugin } from '../src/plugin'; import { NotificationsSidebarItem } from '../src'; -// TODO: How to sign in here as guest user? createDevApp() .registerPlugin(notificationsPlugin) .addPage({ element: , path: '/notifications', - sidebarItem: , }) + .addSidebarItem() .render(); diff --git a/plugins/signals-backend/dev/index.ts b/plugins/signals-backend/dev/index.ts index ae98e54eed..39021a9fcd 100644 --- a/plugins/signals-backend/dev/index.ts +++ b/plugins/signals-backend/dev/index.ts @@ -53,6 +53,8 @@ const signalDebug = createBackendPlugin({ const backend = createBackend(); backend.add(import('@backstage/plugin-events-backend/alpha')); +backend.add(import('@backstage/plugin-auth-backend')); +backend.add(import('@backstage/plugin-auth-backend-module-guest-provider')); backend.add(import('../src')); backend.add(signalDebug); diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index 923b4bb150..a6ebb228e4 100644 --- a/plugins/signals-backend/package.json +++ b/plugins/signals-backend/package.json @@ -47,6 +47,8 @@ "devDependencies": { "@backstage/backend-defaults": "workspace:^", "@backstage/cli": "workspace:^", + "@backstage/plugin-auth-backend": "workspace:^", + "@backstage/plugin-auth-backend-module-guest-provider": "workspace:^", "@backstage/plugin-events-backend": "workspace:^", "@types/supertest": "^2.0.8", "msw": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index e392282e15..893930d505 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9285,6 +9285,8 @@ __metadata: "@backstage/backend-plugin-api": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/plugin-auth-backend": "workspace:^" + "@backstage/plugin-auth-backend-module-guest-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-events-backend": "workspace:^" "@backstage/plugin-events-node": "workspace:^"