diff --git a/.changeset/swappable-notification-description.md b/.changeset/swappable-notification-description.md new file mode 100644 index 0000000000..abfa470035 --- /dev/null +++ b/.changeset/swappable-notification-description.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +The notification description used in the notifications table is now a swappable component, so that apps can replace its rendering with a custom implementation. diff --git a/plugins/notifications/report.api.md b/plugins/notifications/report.api.md index ff39a03a8a..627ea51e82 100644 --- a/plugins/notifications/report.api.md +++ b/plugins/notifications/report.api.md @@ -14,6 +14,7 @@ import { NotificationSettings } from '@backstage/plugin-notifications-common'; import { NotificationSeverity } from '@backstage/plugin-notifications-common'; import { NotificationStatus } from '@backstage/plugin-notifications-common'; import { RouteRef } from '@backstage/core-plugin-api'; +import { SwappableComponentRef } from '@backstage/frontend-plugin-api'; import { TableProps } from '@backstage/core-components'; import { TranslationRef } from '@backstage/frontend-plugin-api'; @@ -49,6 +50,20 @@ export type GetTopicsResponse = { topics: string[]; }; +// @public +export const NotificationDescription: { + (props: NotificationDescriptionProps): JSX.Element | null; + ref: SwappableComponentRef< + NotificationDescriptionProps, + NotificationDescriptionProps + >; +}; + +// @public +export interface NotificationDescriptionProps { + description: string; +} + // @public (undocumented) export interface NotificationsApi { // (undocumented) diff --git a/plugins/notifications/src/components/NotificationsTable/DefaultNotificationDescription.tsx b/plugins/notifications/src/components/NotificationsTable/DefaultNotificationDescription.tsx new file mode 100644 index 0000000000..9524937380 --- /dev/null +++ b/plugins/notifications/src/components/NotificationsTable/DefaultNotificationDescription.tsx @@ -0,0 +1,61 @@ +/* + * Copyright 2025 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 { Text, Button } from '@backstage/ui'; +import { useState } from 'react'; +import type { NotificationDescriptionProps } from './NotificationDescription'; + +const MAX_LENGTH = 100; + +export const DefaultNotificationDescription = ( + props: NotificationDescriptionProps, +) => { + const { description } = props; + const [shown, setShown] = useState(false); + const isLong = description.length > MAX_LENGTH; + + if (!isLong) { + return {description}; + } + + if (shown) { + return ( + + {description}{' '} + + + ); + } + return ( + + {description.substring(0, MAX_LENGTH)}...{' '} + + + ); +}; diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx b/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx index 760473d064..c284e1e85a 100644 --- a/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx +++ b/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx @@ -13,46 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Text, Button } from '@backstage/ui'; -import { useState } from 'react'; +import { createSwappableComponent } from '@backstage/frontend-plugin-api'; -const MAX_LENGTH = 100; +/** + * Props for the {@link NotificationDescription} swappable component. + * + * @public + */ +export interface NotificationDescriptionProps { + /** + * The plain-text description of the notification. + */ + description: string; +} -export const NotificationDescription = (props: { description: string }) => { - const { description } = props; - const [shown, setShown] = useState(false); - const isLong = description.length > MAX_LENGTH; - - if (!isLong) { - return {description}; - } - - if (shown) { - return ( - - {description}{' '} - - - ); - } - return ( - - {description.substring(0, MAX_LENGTH)}...{' '} - - - ); -}; +/** + * Swappable component that renders the description of a notification in the + * notifications table. Apps can override this to customize how descriptions + * are displayed. + * + * @public + */ +export const NotificationDescription = + createSwappableComponent({ + id: 'notifications.notification-description', + loader: () => + import('./DefaultNotificationDescription').then( + m => m.DefaultNotificationDescription, + ), + }); diff --git a/plugins/notifications/src/components/NotificationsTable/index.ts b/plugins/notifications/src/components/NotificationsTable/index.ts index a1fb7a25c6..81d6cacce9 100644 --- a/plugins/notifications/src/components/NotificationsTable/index.ts +++ b/plugins/notifications/src/components/NotificationsTable/index.ts @@ -14,3 +14,4 @@ * limitations under the License. */ export * from './NotificationsTable'; +export * from './NotificationDescription';