Merge pull request #30695 from drodil/duplicate_origins_fix

fix: duplicate origins with multiple notification processors
This commit is contained in:
Patrik Oldsberg
2025-08-15 19:54:34 +02:00
committed by GitHub
4 changed files with 102 additions and 30 deletions
@@ -15,7 +15,6 @@
*/
import {
ChannelSetting,
isNotificationsEnabledFor,
NotificationSettings,
OriginSetting,
@@ -30,7 +29,6 @@ import { NoBorderTableCell } from './NoBorderTableCell';
import { useNotificationFormat } from './UserNotificationSettingsCard';
export const OriginRow = (props: {
channel: ChannelSetting;
origin: OriginSetting;
settings: NotificationSettings;
handleChange: (
@@ -42,8 +40,7 @@ export const OriginRow = (props: {
open: boolean;
handleRowToggle: (originId: string) => void;
}) => {
const { channel, origin, settings, handleChange, open, handleRowToggle } =
props;
const { origin, settings, handleChange, open, handleRowToggle } = props;
const { formatOriginName } = useNotificationFormat();
return (
<TableRow>
@@ -67,7 +64,7 @@ export const OriginRow = (props: {
{settings.channels.map(ch => (
<NoBorderTableCell key={ch.id} align="center">
<Tooltip
title={`Enable or disable ${channel.id.toLocaleLowerCase(
title={`Enable or disable ${ch.id.toLocaleLowerCase(
'en-US',
)} notifications from ${formatOriginName(origin.id)}`}
>
@@ -0,0 +1,58 @@
/*
* 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 { render, screen } from '@testing-library/react';
import { UserNotificationSettingsPanel } from './UserNotificationSettingsPanel';
import { NotificationFormatProvider } from './UserNotificationSettingsCard.tsx';
describe('UserNotificationSettingsPanel', () => {
it('renders each origin only once even if present in multiple channels', () => {
const settings = {
channels: [
{
id: 'email',
origins: [
{
id: 'origin-1',
enabled: true,
topics: [],
},
],
},
{
id: 'slack',
origins: [
{
id: 'origin-1',
enabled: false,
topics: [],
},
],
},
],
};
render(
<NotificationFormatProvider originMap={{}} topicMap={{}}>
<UserNotificationSettingsPanel
settings={settings}
onChange={() => {}}
/>
</NotificationFormatProvider>,
);
const originRows = screen.getAllByText('Origin 1');
expect(originRows).toHaveLength(1);
});
});
@@ -15,7 +15,10 @@
*/
import { useState } from 'react';
import { NotificationSettings } from '@backstage/plugin-notifications-common';
import {
NotificationSettings,
OriginSetting,
} from '@backstage/plugin-notifications-common';
import Table from '@material-ui/core/Table';
import MuiTableCell from '@material-ui/core/TableCell';
import { withStyles } from '@material-ui/core/styles';
@@ -109,6 +112,18 @@ export const UserNotificationSettingsPanel = (props: {
);
}
const uniqueOriginsMap = settings.channels
.flatMap(channel => channel.origins)
.reduce((map, origin) => {
if (!map.has(origin.id)) {
map.set(origin.id, origin);
}
return map;
}, new Map<string, OriginSetting>())
.values();
const uniqueOrigins = Array.from(uniqueOriginsMap);
return (
<Table>
<TableHead>
@@ -130,30 +145,27 @@ export const UserNotificationSettingsPanel = (props: {
</TableRow>
</TableHead>
<TableBody>
{settings.channels.map(channel =>
channel.origins.flatMap(origin => [
<OriginRow
key={origin.id}
channel={channel}
origin={origin}
settings={settings}
open={expandedRows.has(origin.id)}
handleChange={handleChange}
handleRowToggle={handleRowToggle}
/>,
...(expandedRows.has(origin.id)
? origin.topics?.map(topic => (
<TopicRow
key={`${origin.id}-${topic.id}`}
topic={topic}
origin={origin}
settings={settings}
handleChange={handleChange}
/>
)) || []
: []),
]),
)}
{uniqueOrigins.flatMap(origin => [
<OriginRow
key={origin.id}
origin={origin}
settings={settings}
open={expandedRows.has(origin.id)}
handleChange={handleChange}
handleRowToggle={handleRowToggle}
/>,
...(expandedRows.has(origin.id)
? origin.topics?.map(topic => (
<TopicRow
key={`${origin.id}-${topic.id}`}
topic={topic}
origin={origin}
settings={settings}
handleChange={handleChange}
/>
)) || []
: []),
])}
</TableBody>
</Table>
);