Merge pull request #31532 from drodil/notifications_ui_improvements2
fix(notifications): ui improvements
This commit is contained in:
@@ -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.
|
||||
@@ -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 (
|
||||
<>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Filters</Typography>
|
||||
<Divider variant="fullWidth" />
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<FormControl fullWidth variant="outlined" size="small">
|
||||
<InputLabel id="notifications-filter-view">View</InputLabel>
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 <Typography variant="body2">{description}</Typography>;
|
||||
}
|
||||
|
||||
if (shown) {
|
||||
return (
|
||||
<Typography variant="body2">
|
||||
{description}{' '}
|
||||
<Button
|
||||
variant="text"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setShown(false);
|
||||
}}
|
||||
>
|
||||
Show less
|
||||
</Button>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Typography variant="body2">
|
||||
{description.substring(0, MAX_LENGTH)}...{' '}
|
||||
<Button
|
||||
variant="text"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setShown(true);
|
||||
}}
|
||||
>
|
||||
Show more
|
||||
</Button>
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
@@ -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 = ({
|
||||
)}
|
||||
</Typography>
|
||||
{notification.payload.description ? (
|
||||
<Typography variant="body2" className={classes.description}>
|
||||
{notification.payload.description}
|
||||
</Typography>
|
||||
<NotificationDescription
|
||||
description={notification.payload.description}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Typography variant="caption">
|
||||
@@ -318,7 +317,6 @@ export const NotificationsTable = ({
|
||||
onMarkAllRead,
|
||||
onNotificationsSelectChange,
|
||||
classes.severityItem,
|
||||
classes.description,
|
||||
classes.broadcastIcon,
|
||||
classes.notificationInfoRow,
|
||||
markAsReadOnLinkOpen,
|
||||
|
||||
@@ -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={
|
||||
<Checkbox
|
||||
color="primary"
|
||||
disabled={!totalCount}
|
||||
checked={count > 0}
|
||||
indeterminate={count > 0 && totalCount !== count}
|
||||
onChange={onSelectAll}
|
||||
/>
|
||||
<Tooltip title="Select all">
|
||||
<Checkbox
|
||||
color="primary"
|
||||
disabled={!totalCount}
|
||||
checked={count > 0}
|
||||
indeterminate={count > 0 && totalCount !== count}
|
||||
onChange={onSelectAll}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user