Merge pull request #34019 from backstage/freben/swappable-notification-description

Make NotificationDescription a swappable component
This commit is contained in:
Fredrik Adelöw
2026-04-22 10:34:28 +02:00
committed by GitHub
5 changed files with 109 additions and 41 deletions
@@ -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.
+15
View File
@@ -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)
@@ -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 <Text variant="body-small">{description}</Text>;
}
if (shown) {
return (
<Text variant="body-small">
{description}{' '}
<Button
variant="tertiary"
onPress={() => {
setShown(false);
}}
>
Show less
</Button>
</Text>
);
}
return (
<Text variant="body-small">
{description.substring(0, MAX_LENGTH)}...{' '}
<Button
variant="tertiary"
onPress={() => {
setShown(true);
}}
>
Show more
</Button>
</Text>
);
};
@@ -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 <Text variant="body-small">{description}</Text>;
}
if (shown) {
return (
<Text variant="body-small">
{description}{' '}
<Button
variant="tertiary"
onPress={() => {
setShown(false);
}}
>
Show less
</Button>
</Text>
);
}
return (
<Text variant="body-small">
{description.substring(0, MAX_LENGTH)}...{' '}
<Button
variant="tertiary"
onPress={() => {
setShown(true);
}}
>
Show more
</Button>
</Text>
);
};
/**
* 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<NotificationDescriptionProps>({
id: 'notifications.notification-description',
loader: () =>
import('./DefaultNotificationDescription').then(
m => m.DefaultNotificationDescription,
),
});
@@ -14,3 +14,4 @@
* limitations under the License.
*/
export * from './NotificationsTable';
export * from './NotificationDescription';