diff --git a/.changeset/rotten-carrots-relax.md b/.changeset/rotten-carrots-relax.md new file mode 100644 index 0000000000..339a5cdd6b --- /dev/null +++ b/.changeset/rotten-carrots-relax.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Move long notification descriptions behind `Show more/less` button. + +This improves readability of the notifications list by preventing long descriptions from taking up too much space +or rendering very small scrollable areas. diff --git a/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx b/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx index 7e3f519b0c..e8fa422fa1 100644 --- a/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx +++ b/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx @@ -14,14 +14,11 @@ * limitations under the License. */ import { ChangeEvent } from 'react'; - -import Divider from '@material-ui/core/Divider'; import FormControl from '@material-ui/core/FormControl'; import Grid from '@material-ui/core/Grid'; import InputLabel from '@material-ui/core/InputLabel'; import MenuItem from '@material-ui/core/MenuItem'; import Select from '@material-ui/core/Select'; -import Typography from '@material-ui/core/Typography'; import { GetNotificationsOptions } from '../../api'; import { NotificationSeverity } from '@backstage/plugin-notifications-common'; @@ -200,11 +197,6 @@ export const NotificationsFilters = ({ return ( <> - - Filters - - - View diff --git a/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx b/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx index 739e9a1535..69242ac60e 100644 --- a/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx +++ b/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { useState, useMemo, useEffect } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import throttle from 'lodash/throttle'; import { Content, @@ -156,13 +156,17 @@ export const NotificationsPage = (props?: NotificationsPageProps) => { const isUnread = !!value?.[1]?.unread; const allTopics = value?.[2]?.topics; - let tableTitle = `All notifications (${totalCount})`; + let tableTitle = `All notifications `; if (saved) { - tableTitle = `Saved notifications (${totalCount})`; + tableTitle = `Saved notifications`; } else if (unreadOnly === true) { - tableTitle = `Unread notifications (${totalCount})`; + tableTitle = `Unread notifications`; } else if (unreadOnly === false) { - tableTitle = `Read notifications (${totalCount})`; + tableTitle = `Read notifications`; + } + + if (totalCount) { + tableTitle += ` (${totalCount})`; } return ( diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx b/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx new file mode 100644 index 0000000000..762900c2b9 --- /dev/null +++ b/plugins/notifications/src/components/NotificationsTable/NotificationDescription.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 Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import { useState } from 'react'; + +const MAX_LENGTH = 100; + +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)}...{' '} + + + ); +}; diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx index 8ec237e62f..952c18f9f9 100644 --- a/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx +++ b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { useState, useCallback, useMemo, useEffect } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import throttle from 'lodash/throttle'; // @ts-ignore import RelativeTime from 'react-relative-time'; @@ -37,14 +37,11 @@ import { notificationsApiRef } from '../../api'; import { SelectAll } from './SelectAll'; import { BulkActions } from './BulkActions'; import { NotificationIcon } from './NotificationIcon'; +import { NotificationDescription } from './NotificationDescription'; const ThrottleDelayMs = 1000; const useStyles = makeStyles(theme => ({ - description: { - maxHeight: '5rem', - overflow: 'auto', - }, severityItem: { alignContent: 'center', }, @@ -53,8 +50,10 @@ const useStyles = makeStyles(theme => ({ verticalAlign: 'text-bottom', }, notificationInfoRow: { - marginLeft: theme.spacing(0.5), marginRight: theme.spacing(0.5), + '&:not(:first-child)': { + marginLeft: theme.spacing(0.5), + }, }, })); @@ -240,9 +239,9 @@ export const NotificationsTable = ({ )} {notification.payload.description ? ( - - {notification.payload.description} - + ) : null} @@ -318,7 +317,6 @@ export const NotificationsTable = ({ onMarkAllRead, onNotificationsSelectChange, classes.severityItem, - classes.description, classes.broadcastIcon, classes.notificationInfoRow, markAsReadOnLinkOpen, diff --git a/plugins/notifications/src/components/NotificationsTable/SelectAll.tsx b/plugins/notifications/src/components/NotificationsTable/SelectAll.tsx index 9bcc497132..f6d6ff48c9 100644 --- a/plugins/notifications/src/components/NotificationsTable/SelectAll.tsx +++ b/plugins/notifications/src/components/NotificationsTable/SelectAll.tsx @@ -16,6 +16,7 @@ import Checkbox from '@material-ui/core/Checkbox'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import { makeStyles } from '@material-ui/core/styles'; +import Tooltip from '@material-ui/core/Tooltip'; const useStyles = makeStyles({ label: { @@ -23,6 +24,7 @@ const useStyles = makeStyles({ maxWidth: '2rem', '& span': { paddingRight: '0px', + marginRight: '2px', }, }, }); @@ -43,13 +45,15 @@ export const SelectAll = ({ label={count > 0 ? `(${count})` : undefined} className={classes.label} control={ - 0} - indeterminate={count > 0 && totalCount !== count} - onChange={onSelectAll} - /> + + 0} + indeterminate={count > 0 && totalCount !== count} + onChange={onSelectAll} + /> + } /> );