diff --git a/e2e-tests/cypress/tests/integration/channels/channel/browse_channels_spec.ts b/e2e-tests/cypress/tests/integration/channels/channel/browse_channels_spec.ts
index 513d5c5f330..8c4c4575b95 100644
--- a/e2e-tests/cypress/tests/integration/channels/channel/browse_channels_spec.ts
+++ b/e2e-tests/cypress/tests/integration/channels/channel/browse_channels_spec.ts
@@ -245,11 +245,21 @@ describe('Channels', () => {
cy.wrap(el).should('contain', channelType.all);
});
- // * Users should be able to type and search
+ // * Hide Archived is on by default, so search only returns active matches
+ cy.get('#hideArchivedPreferenceCheckbox').should('be.visible').and('have.attr', 'aria-checked', 'true');
cy.get('#searchChannelsTextbox').should('be.visible').type('iv').wait(TIMEOUTS.HALF_SEC);
+ cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 1);
+ cy.get('#moreChannelsList').should('be.visible').within(() => {
+ cy.findByText(newChannel.display_name).should('be.visible');
+ cy.findByText(testArchivedChannel.display_name).should('not.exist');
+ });
+
+ // # Uncheck Hide Archived so archived matches appear in All search results
+ cy.get('#hideArchivedPreferenceCheckbox').click();
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 2);
cy.get('#moreChannelsList').should('be.visible').within(() => {
cy.findByText(newChannel.display_name).should('be.visible');
+ cy.findByText(testArchivedChannel.display_name).should('be.visible');
});
cy.get('#browseChannelsModal').should('be.visible');
diff --git a/e2e-tests/playwright/lib/src/ui/components/channels/browse_channels_modal.ts b/e2e-tests/playwright/lib/src/ui/components/channels/browse_channels_modal.ts
index b539aff730b..f5968a6f293 100644
--- a/e2e-tests/playwright/lib/src/ui/components/channels/browse_channels_modal.ts
+++ b/e2e-tests/playwright/lib/src/ui/components/channels/browse_channels_modal.ts
@@ -8,6 +8,7 @@ export default class BrowseChannelsModal {
readonly container: Locator;
readonly createNewChannelButton: Locator;
+ readonly hideArchivedCheckbox: Locator;
readonly hideJoinedCheckbox: Locator;
readonly searchInput: Locator;
@@ -17,6 +18,7 @@ export default class BrowseChannelsModal {
this.container = container;
this.createNewChannelButton = container.getByRole('button', {name: 'Create New Channel'});
+ this.hideArchivedCheckbox = container.getByRole('checkbox', {name: 'Hide Archived'});
this.hideJoinedCheckbox = container.getByRole('checkbox', {name: 'Hide Joined'});
this.searchInput = container.getByRole('textbox', {name: 'Search channels'});
diff --git a/e2e-tests/playwright/specs/accessibility/channels/browse_channels_dialog.spec.ts b/e2e-tests/playwright/specs/accessibility/channels/browse_channels_dialog.spec.ts
index 2583ecbab75..908e899617b 100644
--- a/e2e-tests/playwright/specs/accessibility/channels/browse_channels_dialog.spec.ts
+++ b/e2e-tests/playwright/specs/accessibility/channels/browse_channels_dialog.spec.ts
@@ -55,6 +55,7 @@ test(
await hideJoinedCheckbox.click();
// # Focus on Create Channel button and tab through elements
+ // Tab order: Close → Search → Channel type filter → Hide Archived → Hide Joined → first channel
const createChannelButton = dialog.createNewChannelButton;
await createChannelButton.focus();
await page.keyboard.press('Tab');
@@ -62,6 +63,7 @@ test(
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
+ await page.keyboard.press('Tab');
// * Verify channel name is highlighted and has proper aria-label
await dialog.toHaveChannelAsNthResult(channel1.name, 0);
@@ -116,6 +118,7 @@ test(
- /status: \\d+ Results/
- status: Channel type filter set to All
- button "Channel type filter"
+ - checkbox "Hide archived channels" [checked]: Hide Archived
- checkbox "Hide joined channels": Hide Joined
- search
`);
diff --git a/webapp/channels/src/components/__snapshots__/searchable_channel_list.test.tsx.snap b/webapp/channels/src/components/__snapshots__/searchable_channel_list.test.tsx.snap
index aa2f6cca105..39deb4f4f83 100644
--- a/webapp/channels/src/components/__snapshots__/searchable_channel_list.test.tsx.snap
+++ b/webapp/channels/src/components/__snapshots__/searchable_channel_list.test.tsx.snap
@@ -75,6 +75,74 @@ exports[`components/SearchableChannelList should match init snapshot 1`] = `
/>
+
{
teamName: 'team_name',
channelsRequestStarted: false,
shouldHideJoinedChannels: false,
+ shouldHideArchivedChannels: true,
accessControlEnabled: false,
myChannelMemberships: {
'channel-id-3': TestHelper.getChannelMembershipMock({
@@ -714,6 +715,230 @@ describe('components/BrowseChannels', () => {
expect(baseProps.actions.getRecommendedChannelsForUser).not.toHaveBeenCalled();
});
+ test('hides archived channels from All search results when shouldHideArchivedChannels is true', async () => {
+ const searchAllChannels = jest.fn(channelActions.searchAllChannels);
+ const props = {...baseProps, shouldHideArchivedChannels: true, actions: {...baseProps.actions, searchAllChannels}};
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ const searchInput = screen.getByPlaceholderText('Search channels');
+ await user.type(searchInput, 'channel');
+
+ await act(async () => {
+ jest.runOnlyPendingTimers();
+ await Promise.resolve();
+ });
+
+ // The active public and private channels are shown, but the archived
+ // channel returned by the search is filtered out under the default
+ // "All" filter — proving only archived rows are removed.
+ await waitFor(() => {
+ expect(screen.getByText('Channel 1')).toBeInTheDocument();
+ expect(screen.getByText('Private')).toBeInTheDocument();
+ });
+ expect(screen.queryByText('Archived')).not.toBeInTheDocument();
+ });
+
+ test('hides archived channels from the browse list by default (no search)', async () => {
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ // baseProps.shouldHideArchivedChannels is true, so the archived channel
+ // ('channel-2') is absent from the default All browse list.
+ expect(screen.getByText('Default Channel')).toBeInTheDocument();
+ expect(screen.queryByText('channel-2')).not.toBeInTheDocument();
+ });
+
+ test('shows archived channels in the browse list when shouldHideArchivedChannels is false (no search)', async () => {
+ const props = {...baseProps, shouldHideArchivedChannels: false};
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ // With the toggle off, the archived channel is mixed into the All list.
+ expect(screen.getByText('Default Channel')).toBeInTheDocument();
+ expect(screen.getByText('channel-2')).toBeInTheDocument();
+ });
+
+ test('shows archived channels in All search results when shouldHideArchivedChannels is false', async () => {
+ const searchAllChannels = jest.fn(channelActions.searchAllChannels);
+ const props = {...baseProps, shouldHideArchivedChannels: false, actions: {...baseProps.actions, searchAllChannels}};
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ const searchInput = screen.getByPlaceholderText('Search channels');
+ await user.type(searchInput, 'channel');
+
+ await act(async () => {
+ jest.runOnlyPendingTimers();
+ await Promise.resolve();
+ });
+
+ // With the toggle off, archived channels are mixed back into the results.
+ await waitFor(() => {
+ expect(screen.getByText('Channel 1')).toBeInTheDocument();
+ expect(screen.getByText('Archived')).toBeInTheDocument();
+ });
+ });
+
+ // Regression: archived private channels used to leak in via the
+ // privateChannels list (which is not filtered by the Hide Archived toggle)
+ // and, with the toggle off, appear twice — once from privateChannels and
+ // once from archivedChannels. The container selector now returns only
+ // active private channels, so archived channels of both types come solely
+ // from `archivedChannels`.
+ const archivedPublicChannel = TestHelper.getChannelMock({
+ id: 'archived-public-id',
+ team_id: 'team_1',
+ display_name: 'Archived Public',
+ name: 'archived-public',
+ type: 'O',
+ delete_at: 123,
+ });
+
+ const archivedPrivateChannel = TestHelper.getChannelMock({
+ id: 'archived-private-id',
+ team_id: 'team_1',
+ display_name: 'Archived Private',
+ name: 'archived-private',
+ type: 'P',
+ delete_at: 456,
+ });
+
+ const bothTypesArchivedProps: Props = {
+ ...baseProps,
+ channels: [defaultChannel],
+ privateChannels: [privateChannel],
+ archivedChannels: [archivedPublicChannel, archivedPrivateChannel],
+ };
+
+ test('hides both archived public and private channels from the All list when the toggle is on', async () => {
+ const props = {...bothTypesArchivedProps, shouldHideArchivedChannels: true};
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(screen.getByText('Default Channel')).toBeInTheDocument();
+ expect(screen.queryByText('Archived Public')).not.toBeInTheDocument();
+ expect(screen.queryByText('Archived Private')).not.toBeInTheDocument();
+ });
+
+ test('shows each archived channel exactly once in the All list when the toggle is off', async () => {
+ const props = {...bothTypesArchivedProps, shouldHideArchivedChannels: false};
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(screen.getByText('Archived Public')).toBeInTheDocument();
+
+ // Exactly one row — previously the archived private channel rendered
+ // twice because it came from both privateChannels and archivedChannels.
+ expect(screen.getAllByText('Archived Private')).toHaveLength(1);
+ });
+
+ test('includes archived channels of the matching type under the Public and Private filters when the toggle is off', async () => {
+ const props = {...bothTypesArchivedProps, shouldHideArchivedChannels: false};
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ await user.click(screen.getByLabelText('Channel type filter'));
+ await user.click(await screen.findByText('Public channels'));
+
+ // Wait on the private row being pruned — "Archived Public" is present in
+ // both the All and Public views, so it isn't a reliable settle signal.
+ await waitFor(() => {
+ expect(screen.queryByText('Archived Private')).not.toBeInTheDocument();
+ });
+ expect(screen.getByText('Archived Public')).toBeInTheDocument();
+
+ await user.click(screen.getByLabelText('Channel type filter'));
+ await user.click(await screen.findByText('Private channels'));
+
+ await waitFor(() => {
+ expect(screen.queryByText('Archived Public')).not.toBeInTheDocument();
+ });
+ expect(screen.getByText('Archived Private')).toBeInTheDocument();
+ });
+
+ test('still shows archived channels when the Archived filter is selected even if shouldHideArchivedChannels is true', async () => {
+ const searchAllChannels = jest.fn(channelActions.searchAllChannels);
+ const props = {...baseProps, shouldHideArchivedChannels: true, actions: {...baseProps.actions, searchAllChannels}};
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ await user.click(screen.getByLabelText('Channel type filter'));
+ await user.click(await screen.findByText('Archived channels'));
+
+ const searchInput = screen.getByPlaceholderText('Search channels');
+ await user.type(searchInput, 'channel');
+
+ await act(async () => {
+ jest.runOnlyPendingTimers();
+ await Promise.resolve();
+ });
+
+ await waitFor(() => {
+ expect(screen.getByText('Archived')).toBeInTheDocument();
+ });
+ expect(screen.queryByText('Channel 1')).not.toBeInTheDocument();
+ });
+
+ test('toggling Hide Archived persists the preference', async () => {
+ const setGlobalItem = jest.fn();
+ const props = {...baseProps, shouldHideArchivedChannels: true, actions: {...baseProps.actions, setGlobalItem}};
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ // Starts checked (hidden by default); unchecking persists 'false'.
+ const hideArchived = screen.getByLabelText('Hide archived channels');
+ expect(hideArchived).toHaveAttribute('aria-checked', 'true');
+
+ await user.click(hideArchived);
+
+ expect(setGlobalItem).toHaveBeenCalledWith('hideArchivedChannels', 'false');
+ });
+
+ test('Hide Archived checkbox is not shown when the Archived filter is selected', async () => {
+ renderWithContext(
);
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(screen.getByLabelText('Hide archived channels')).toBeInTheDocument();
+
+ await user.click(screen.getByLabelText('Channel type filter'));
+ await user.click(await screen.findByText('Archived channels'));
+
+ await waitFor(() => {
+ expect(screen.queryByLabelText('Hide archived channels')).not.toBeInTheDocument();
+ });
+ });
+
// ---------------------------------------------------------------
// Discoverable Private Channels — row state machine + filter chips
// ---------------------------------------------------------------
diff --git a/webapp/channels/src/components/browse_channels/browse_channels.tsx b/webapp/channels/src/components/browse_channels/browse_channels.tsx
index 7a04086701d..bbb62d41419 100644
--- a/webapp/channels/src/components/browse_channels/browse_channels.tsx
+++ b/webapp/channels/src/components/browse_channels/browse_channels.tsx
@@ -87,6 +87,7 @@ export type Props = {
channelsRequestStarted?: boolean;
myChannelMemberships: RelationOneToOne
;
shouldHideJoinedChannels: boolean;
+ shouldHideArchivedChannels: boolean;
rhsState?: RhsState;
rhsOpen?: boolean;
channelsMemberCount?: Record;
@@ -363,7 +364,10 @@ export default class BrowseChannels extends React.PureComponent {
searchedChannels = channels.filter((c) => c.type === Constants.PRIVATE_CHANNEL && this.canSeePrivateChannel(c));
}
if (this.state.filter === Filter.Public) {
- searchedChannels = channels.filter((c) => c.type === Constants.OPEN_CHANNEL && c.delete_at === 0);
+ // Archived public channels are pruned below when the Hide Archived
+ // toggle is on, so don't force delete_at === 0 here — that would
+ // hide them even when the toggle is off.
+ searchedChannels = channels.filter((c) => c.type === Constants.OPEN_CHANNEL);
}
if (this.state.filter === Filter.Archived) {
searchedChannels = channels.filter((c) => c.delete_at !== 0);
@@ -385,6 +389,9 @@ export default class BrowseChannels extends React.PureComponent {
if (this.state.filter === Filter.MyPendingRequests) {
searchedChannels = channels.filter((c) => this.props.myPendingJoinRequests[c.id]);
}
+ if (this.state.filter !== Filter.Archived && this.props.shouldHideArchivedChannels) {
+ searchedChannels = this.getChannelsWithoutArchived(searchedChannels);
+ }
if (this.props.shouldHideJoinedChannels) {
searchedChannels = this.getChannelsWithoutJoined(searchedChannels);
}
@@ -428,10 +435,18 @@ export default class BrowseChannels extends React.PureComponent {
this.props.actions.setGlobalItem(StoragePrefixes.HIDE_JOINED_CHANNELS, shouldHideJoinedChannels.toString());
};
+ handleShowArchivedChannelsPreference = (shouldHideArchivedChannels: boolean) => {
+ // search again when toggling to update search results
+ this.search(this.state.searchTerm);
+ this.props.actions.setGlobalItem(StoragePrefixes.HIDE_ARCHIVED_CHANNELS, shouldHideArchivedChannels.toString());
+ };
+
getChannelsWithoutJoined = (channelList: Channel[]) => channelList.filter((channel) => !this.isMemberOfChannel(channel.id));
+ getChannelsWithoutArchived = (channelList: Channel[]) => channelList.filter((channel) => channel.delete_at === 0);
+
getActiveChannels = () => {
- const {channels, archivedChannels, shouldHideJoinedChannels, privateChannels, myPendingJoinRequests} = this.props;
+ const {channels, archivedChannels, shouldHideJoinedChannels, shouldHideArchivedChannels, privateChannels, myPendingJoinRequests} = this.props;
const {search, searchedChannels, filter, recommendedChannels, discoverableChannels} = this.state;
// Discoverable private channels the user is not yet a member of. These
@@ -456,11 +471,23 @@ export default class BrowseChannels extends React.PureComponent {
// appear in the default browse view, not only under the Discoverable
// filter. privateChannels-sourced rows are already in allChannels.
const extraDiscoverable = discoverableChannels.filter((c) => !privateChannels.some((p) => p.id === c.id));
- const allChannels = channels.concat(privateChannels, extraDiscoverable).sort((a, b) => a.display_name.localeCompare(b.display_name));
+
+ // `archivedChannels` holds both archived public and private channels.
+ // When the toggle is on they are dropped everywhere except the explicit
+ // Archived filter; when it is off they are folded back into the matching
+ // type-specific list so Public/Private show their archived rows too.
+ const visibleArchivedChannels = shouldHideArchivedChannels ? [] : archivedChannels;
+ const publicChannels = channels.concat(visibleArchivedChannels.filter((c) => c.type === Constants.OPEN_CHANNEL));
+ const visiblePrivateChannels = privateChannels.concat(visibleArchivedChannels.filter((c) => c.type === Constants.PRIVATE_CHANNEL));
+
+ const allChannels = channels.
+ concat(privateChannels, extraDiscoverable).
+ concat(visibleArchivedChannels).
+ sort((a, b) => a.display_name.localeCompare(b.display_name));
const allChannelsWithoutJoined = this.getChannelsWithoutJoined(allChannels);
- const publicChannelsWithoutJoined = this.getChannelsWithoutJoined(channels);
+ const publicChannelsWithoutJoined = this.getChannelsWithoutJoined(publicChannels);
const archivedChannelsWithoutJoined = this.getChannelsWithoutJoined(archivedChannels);
- const privateChannelsWithoutJoined = this.getChannelsWithoutJoined(privateChannels);
+ const privateChannelsWithoutJoined = this.getChannelsWithoutJoined(visiblePrivateChannels);
const recommendedChannelsWithoutJoined = this.getChannelsWithoutJoined(recommendedChannels);
// Channels the current user has pending requests against. The
@@ -479,8 +506,8 @@ export default class BrowseChannels extends React.PureComponent {
const filterOptions: Record = {
[Filter.All]: shouldHideJoinedChannels ? allChannelsWithoutJoined : allChannels,
[Filter.Archived]: shouldHideJoinedChannels ? archivedChannelsWithoutJoined : archivedChannels,
- [Filter.Private]: shouldHideJoinedChannels ? privateChannelsWithoutJoined : privateChannels,
- [Filter.Public]: shouldHideJoinedChannels ? publicChannelsWithoutJoined : channels,
+ [Filter.Private]: shouldHideJoinedChannels ? privateChannelsWithoutJoined : visiblePrivateChannels,
+ [Filter.Public]: shouldHideJoinedChannels ? publicChannelsWithoutJoined : publicChannels,
[Filter.Recommended]: shouldHideJoinedChannels ? recommendedChannelsWithoutJoined : recommendedChannels,
[Filter.Discoverable]: discoverableNonMember,
[Filter.MyPendingRequests]: myPending,
@@ -498,7 +525,7 @@ export default class BrowseChannels extends React.PureComponent {
};
render() {
- const {teamId, channelsRequestStarted, shouldHideJoinedChannels} = this.props;
+ const {teamId, channelsRequestStarted, shouldHideJoinedChannels, shouldHideArchivedChannels} = this.props;
const {search, serverError: serverErrorState, searching} = this.state;
this.activeChannels = this.getActiveChannels();
@@ -566,6 +593,8 @@ export default class BrowseChannels extends React.PureComponent {
closeModal={this.props.actions.closeModal}
hideJoinedChannelsPreference={this.handleShowJoinedChannelsPreference}
rememberHideJoinedChannelsChecked={shouldHideJoinedChannels}
+ hideArchivedChannelsPreference={this.handleShowArchivedChannelsPreference}
+ rememberHideArchivedChannelsChecked={shouldHideArchivedChannels}
channelsMemberCount={this.props.channelsMemberCount}
/>
{serverError}
diff --git a/webapp/channels/src/components/browse_channels/index.ts b/webapp/channels/src/components/browse_channels/index.ts
index 03cdbc62fe7..6492617f345 100644
--- a/webapp/channels/src/components/browse_channels/index.ts
+++ b/webapp/channels/src/components/browse_channels/index.ts
@@ -52,12 +52,18 @@ const getArchivedOtherChannels = createSelector(
const getPrivateChannelsSelector = createSelector(
'getPrivateChannelsSelector',
getChannelsInCurrentTeam,
- (channels: Channel[]) => channels && channels.filter((c) => c.type === Constants.PRIVATE_CHANNEL),
+
+ // Active private channels only. Archived private channels are surfaced
+ // exclusively through `archivedChannels`; including them here would both
+ // leak them past the Hide Archived toggle and double them up alongside the
+ // archived list.
+ (channels: Channel[]) => channels && channels.filter((c) => c.delete_at === 0 && c.type === Constants.PRIVATE_CHANNEL),
);
function mapStateToProps(state: GlobalState) {
const team = getCurrentTeam(state);
const getGlobalItem = makeGetGlobalItem(StoragePrefixes.HIDE_JOINED_CHANNELS, 'false');
+ const getHideArchivedItem = makeGetGlobalItem(StoragePrefixes.HIDE_ARCHIVED_CHANNELS, 'true');
return {
channels: getChannelsWithoutArchived(state) || [],
@@ -69,6 +75,7 @@ function mapStateToProps(state: GlobalState) {
channelsRequestStarted: state.requests.channels.getChannels.status === RequestStatus.STARTED,
myChannelMemberships: getMyChannelMemberships(state) || {},
shouldHideJoinedChannels: getGlobalItem(state) === 'true',
+ shouldHideArchivedChannels: getHideArchivedItem(state) === 'true',
rhsState: getRhsState(state),
rhsOpen: getIsRhsOpen(state),
channelsMemberCount: getChannelsMemberCountSelector(state),
diff --git a/webapp/channels/src/components/searchable_channel_list.test.tsx b/webapp/channels/src/components/searchable_channel_list.test.tsx
index 4e300c92bae..a9604d97df4 100644
--- a/webapp/channels/src/components/searchable_channel_list.test.tsx
+++ b/webapp/channels/src/components/searchable_channel_list.test.tsx
@@ -13,7 +13,7 @@ import {compassIconForName} from 'components/channel_type_icon';
import {SearchableChannelList} from 'components/searchable_channel_list';
import {type MockIntl} from 'tests/helpers/intl-test-helper';
-import {renderWithContext, screen} from 'tests/react_testing_utils';
+import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import {Filter} from './browse_channels/browse_channels';
@@ -48,10 +48,12 @@ describe('components/SearchableChannelList', () => {
toggleArchivedChannels: jest.fn(),
closeModal: jest.fn(),
hideJoinedChannelsPreference: jest.fn(),
+ hideArchivedChannelsPreference: jest.fn(),
changeFilter: jest.fn(),
myChannelMemberships: {},
canShowArchivedChannels: false,
rememberHideJoinedChannelsChecked: false,
+ rememberHideArchivedChannelsChecked: true,
noResultsText: <>{'no channel found'}>,
filter: Filter.All,
intl: {
@@ -98,6 +100,48 @@ describe('components/SearchableChannelList', () => {
expect(baseProps.search).toBeDefined();
});
+ test('renders the Hide Archived checkbox reflecting the persisted preference', () => {
+ renderWithContext(
+ ,
+ initialState,
+ );
+
+ const hideArchived = screen.getByLabelText('Hide archived channels');
+ expect(hideArchived).toBeInTheDocument();
+ expect(hideArchived).toHaveAttribute('aria-checked', 'true');
+ });
+
+ test('does not render the Hide Archived checkbox when the Archived filter is active', () => {
+ renderWithContext(
+ ,
+ initialState,
+ );
+
+ expect(screen.queryByLabelText('Hide archived channels')).not.toBeInTheDocument();
+ });
+
+ test('clicking the Hide Archived checkbox toggles the preference', async () => {
+ const hideArchivedChannelsPreference = jest.fn();
+ renderWithContext(
+ ,
+ initialState,
+ );
+
+ const hideArchived = screen.getByLabelText('Hide archived channels');
+ expect(hideArchived).toHaveAttribute('aria-checked', 'false');
+
+ await userEvent.click(hideArchived);
+
+ expect(hideArchivedChannelsPreference).toHaveBeenCalledWith(true);
+ });
+
test('should render ArchiveOutlineIcon for archived public channels', () => {
const channels = [
{
diff --git a/webapp/channels/src/components/searchable_channel_list.tsx b/webapp/channels/src/components/searchable_channel_list.tsx
index b6c154e5d00..fd2b42fe45a 100644
--- a/webapp/channels/src/components/searchable_channel_list.tsx
+++ b/webapp/channels/src/components/searchable_channel_list.tsx
@@ -42,6 +42,8 @@ interface Props extends WrappedComponentProps {
closeModal: (modalId: string) => void;
hideJoinedChannelsPreference: (shouldHideJoinedChannels: boolean) => void;
rememberHideJoinedChannelsChecked: boolean;
+ hideArchivedChannelsPreference: (shouldHideArchivedChannels: boolean) => void;
+ rememberHideArchivedChannelsChecked: boolean;
loading?: boolean;
channelsMemberCount?: Record;
showRecommendedFilter?: boolean;
@@ -451,6 +453,9 @@ export class SearchableChannelList extends React.PureComponent {
this.props.hideJoinedChannelsPreference(true);
}
};
+ handleArchivedChecked = () => {
+ this.props.hideArchivedChannelsPreference(!this.props.rememberHideArchivedChannelsChecked);
+ };
getEmptyStateMessage = () => {
if (this.state.channelSearchValue.length > 0) {
return (
@@ -851,6 +856,34 @@ export class SearchableChannelList extends React.PureComponent {
);
+ // The archived filter explicitly asks for archived channels, so hiding
+ // them there would leave an empty list — only offer the toggle elsewhere.
+ const hideArchivedButtonClass = classNames('get-app__checkbox', {checked: this.props.rememberHideArchivedChannelsChecked});
+ const hideArchivedPreferenceCheckbox = this.props.filter === Filter.Archived ? null : (
+