MM-68536: Show actual remote names in system console channel list (#36298)

* MM-68536: Show actual remote names in system console channel list

  The system console "Channels" list rendered SharedChannelIndicator
  without a remoteNames prop, so every shared channel showed the generic
  "Shared with trusted organizations" fallback even after PR 35908.

  Add a small connected wrapper that selects remote names via
  getRemoteNamesForChannel and dispatches fetchChannelRemotes on mount,
  mirroring the pattern used by the LHS sidebar. The shared_channel_remote_updated
  websocket event added in PR 35908 already refreshes the same Redux slice,
  so the system console list now stays in sync automatically.
This commit is contained in:
Doug Lauder
2026-05-04 14:53:57 -04:00
committed by GitHub
parent 49260c4289
commit 022acb74c5
3 changed files with 118 additions and 3 deletions
@@ -0,0 +1,73 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {renderWithContext} from 'tests/react_testing_utils';
import AdminChannelSharedIndicator from './admin_channel_shared_indicator';
jest.mock('mattermost-redux/actions/shared_channels', () => ({
fetchChannelRemotes: jest.fn(() => ({type: 'MOCK_FETCH_CHANNEL_REMOTES'})),
}));
jest.mock('mattermost-redux/selectors/entities/shared_channels', () => ({
getRemoteNamesForChannel: jest.fn(() => [] as string[]),
}));
jest.mock('components/shared_channel_indicator', () => {
return jest.fn(() => <i data-testid='SharedChannelIcon'/>);
});
describe('admin_console/team_channel_settings/channel/list/AdminChannelSharedIndicator', () => {
beforeEach(() => {
jest.clearAllMocks();
});
test('fetches remotes on mount when remoteNames is empty', () => {
const {getRemoteNamesForChannel} = require('mattermost-redux/selectors/entities/shared_channels');
const {fetchChannelRemotes} = require('mattermost-redux/actions/shared_channels');
getRemoteNamesForChannel.mockReturnValue([]);
renderWithContext(<AdminChannelSharedIndicator channelId='channel-1'/>);
expect(fetchChannelRemotes).toHaveBeenCalledTimes(1);
expect(fetchChannelRemotes).toHaveBeenCalledWith('channel-1');
});
test('does not fetch remotes when remoteNames is already populated', () => {
const {getRemoteNamesForChannel} = require('mattermost-redux/selectors/entities/shared_channels');
const {fetchChannelRemotes} = require('mattermost-redux/actions/shared_channels');
getRemoteNamesForChannel.mockReturnValue(['Org A', 'Org B']);
renderWithContext(<AdminChannelSharedIndicator channelId='channel-1'/>);
expect(fetchChannelRemotes).not.toHaveBeenCalled();
});
test('passes remoteNames from the selector to SharedChannelIndicator', () => {
const {getRemoteNamesForChannel} = require('mattermost-redux/selectors/entities/shared_channels');
const SharedChannelIndicator = require('components/shared_channel_indicator');
getRemoteNamesForChannel.mockReturnValue(['Org A', 'Org B']);
renderWithContext(<AdminChannelSharedIndicator channelId='channel-1'/>);
const props = SharedChannelIndicator.mock.calls[0][0];
expect(props.remoteNames).toEqual(['Org A', 'Org B']);
expect(props.withTooltip).toBe(true);
});
test('forwards the className prop to SharedChannelIndicator', () => {
const SharedChannelIndicator = require('components/shared_channel_indicator');
renderWithContext(
<AdminChannelSharedIndicator
channelId='channel-1'
className='channel-icon'
/>,
);
const props = SharedChannelIndicator.mock.calls[0][0];
expect(props.className).toBe('channel-icon');
});
});
@@ -0,0 +1,41 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useEffect} from 'react';
import {shallowEqual, useDispatch, useSelector} from 'react-redux';
import {fetchChannelRemotes} from 'mattermost-redux/actions/shared_channels';
import {getRemoteNamesForChannel} from 'mattermost-redux/selectors/entities/shared_channels';
import SharedChannelIndicator from 'components/shared_channel_indicator';
import type {GlobalState} from 'types/store';
type Props = {
channelId: string;
className?: string;
};
const AdminChannelSharedIndicator: React.FC<Props> = ({channelId, className}) => {
const dispatch = useDispatch();
const remoteNames = useSelector(
(state: GlobalState) => getRemoteNamesForChannel(state, channelId),
shallowEqual,
);
useEffect(() => {
if (channelId && remoteNames.length === 0) {
dispatch(fetchChannelRemotes(channelId));
}
}, [channelId, remoteNames.length, dispatch]);
return (
<SharedChannelIndicator
className={className}
withTooltip={true}
remoteNames={remoteNames}
/>
);
};
export default AdminChannelSharedIndicator;
@@ -15,11 +15,12 @@ import type {Row, Column} from 'components/admin_console/data_grid/data_grid';
import type {FilterOptions} from 'components/admin_console/filter/filter';
import TeamFilterDropdown from 'components/admin_console/filter/team_filter_dropdown';
import {PAGE_SIZE} from 'components/admin_console/team_channel_settings/abstract_list';
import SharedChannelIndicator from 'components/shared_channel_indicator';
import {getHistory} from 'utils/browser_history';
import {getChannelIconComponent} from 'utils/channel_utils';
import AdminChannelSharedIndicator from './admin_channel_shared_indicator';
import './channel_list.scss';
export interface ChannelListProps {
@@ -195,9 +196,9 @@ export default class ChannelList extends React.PureComponent<ChannelListProps, C
);
const sharedChannelIcon = channel.shared ? (
<SharedChannelIndicator
<AdminChannelSharedIndicator
channelId={channel.id}
className='channel-icon'
withTooltip={true}
/>
) : null;