From 86359529c98b1f41c263bc16d2c11a3ad2333de9 Mon Sep 17 00:00:00 2001
From: Ivy Gesare <83957195+Gesare5@users.noreply.github.com>
Date: Fri, 23 Aug 2024 14:40:38 +0300
Subject: [PATCH] [MM-60076] Convert
./components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.tsx
from Class Component to Function Component (#27995)
* Change: Convert sidebar_base_channel to functional component
Change: Update sidebar_base_channel test
* Change: Add waitFor method to fix failing tests
* Add: Sidebar_base_channel_icon component
Change: Use useCallback to cache the handleLeave functions
Change: Update test snapshots
* Change: getIcon to channelIcon
Change: Add actions.leaveChannel and actions.openModal as useCallback dependencies
Change: Remove else statements
---
.../sidebar_base_channel.test.tsx.snap | 20 +--
.../sidebar_base_channel.test.tsx | 62 ++++++++--
.../sidebar_base_channel.tsx | 117 ++++++++----------
.../sidebar_base_channel_icon.tsx | 43 +++++++
4 files changed, 155 insertions(+), 87 deletions(-)
create mode 100644 webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel_icon.tsx
diff --git a/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/__snapshots__/sidebar_base_channel.test.tsx.snap b/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/__snapshots__/sidebar_base_channel.test.tsx.snap
index 5c30f53ee5..c90ecae0f0 100644
--- a/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/__snapshots__/sidebar_base_channel.test.tsx.snap
+++ b/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/__snapshots__/sidebar_base_channel.test.tsx.snap
@@ -24,8 +24,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
}
channelLeaveHandler={[Function]}
icon={
-
}
label="channel_display_name"
@@ -57,8 +58,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
}
channelLeaveHandler={[Function]}
icon={
-
}
label="channel_display_name"
@@ -91,10 +93,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
}
channelLeaveHandler={[Function]}
icon={
-
}
label="channel_display_name"
@@ -127,10 +128,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
}
channelLeaveHandler={[Function]}
icon={
-
}
label="channel_display_name"
diff --git a/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.test.tsx b/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.test.tsx
index 254afda990..85bc744a1e 100644
--- a/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.test.tsx
+++ b/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.test.tsx
@@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import {screen, waitFor} from '@testing-library/react';
import {shallow} from 'enzyme';
import React from 'react';
@@ -8,6 +9,8 @@ import type {ChannelType} from '@mattermost/types/channels';
import SidebarBaseChannel from 'components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel';
+import {renderWithContext, userEvent} from 'tests/react_testing_utils';
+
describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
const baseProps = {
channel: {
@@ -91,25 +94,64 @@ describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
expect(wrapper).toMatchSnapshot();
});
- test('expect callback to be called when leave public channel ', () => {
- const callback = jest.fn();
- const wrapper = shallow();
- wrapper.instance().handleLeavePublicChannel(callback);
- expect(callback).toBeCalled();
+ test('expect leaveChannel to be called when leave public channel ', async () => {
+ const mockfn = jest.fn();
+
+ const props = {
+ ...baseProps,
+ channel: {
+ ...baseProps.channel,
+ type: 'O' as ChannelType,
+ shared: true,
+ name: 'l',
+ },
+ actions: {
+ leaveChannel: mockfn,
+ openModal: jest.fn(),
+ },
+ };
+
+ renderWithContext();
+
+ const optionsBtn = screen.getByRole('button');
+ expect(optionsBtn.classList).toContain('SidebarMenu_menuButton');
+
+ await userEvent.click(optionsBtn); // open options
+ const leaveOption: HTMLElement = screen.getByText('Leave Channel').parentElement!;
+
+ await userEvent.click(leaveOption);
+ await waitFor(() => {
+ expect(mockfn).toHaveBeenCalledTimes(1);
+ });
});
- test('expect callback to be called when leave private channel ', () => {
- const callback = jest.fn();
+ test('expect openModal to be called when leave private channel ', async () => {
+ const mockfn = jest.fn();
+
const props = {
...baseProps,
channel: {
...baseProps.channel,
type: 'P' as ChannelType,
+ name: 'l',
+ },
+ actions: {
+ leaveChannel: jest.fn(),
+ openModal: mockfn,
},
};
- const wrapper = shallow();
- wrapper.instance().handleLeavePrivateChannel(callback);
- expect(callback).toBeCalled();
+ renderWithContext();
+
+ const optionsBtn = screen.getByRole('button');
+ expect(optionsBtn.classList).toContain('SidebarMenu_menuButton');
+
+ await userEvent.click(optionsBtn); // open options
+ const leaveOption: HTMLElement = screen.getByText('Leave Channel').parentElement!;
+
+ await userEvent.click(leaveOption);
+ await waitFor(() => {
+ expect(mockfn).toHaveBeenCalledTimes(1);
+ });
});
});
diff --git a/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.tsx b/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.tsx
index 40637e0e6b..2bde4b43b7 100644
--- a/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.tsx
+++ b/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel.tsx
@@ -1,18 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React from 'react';
+import React, {useCallback} from 'react';
+import {useIntl} from 'react-intl';
import type {Channel} from '@mattermost/types/channels';
import {trackEvent} from 'actions/telemetry_actions';
import LeaveChannelModal from 'components/leave_channel_modal';
-import SharedChannelIndicator from 'components/shared_channel_indicator';
import SidebarChannelLink from 'components/sidebar/sidebar_channel/sidebar_channel_link';
import Constants, {ModalIdentifiers} from 'utils/constants';
-import {localizeMessage} from 'utils/utils';
+
+import SidebarBaseChannelIcon from './sidebar_base_channel_icon';
import type {PropsFromRedux} from './index';
@@ -21,74 +22,56 @@ export interface Props extends PropsFromRedux {
currentTeamName: string;
}
-export default class SidebarBaseChannel extends React.PureComponent {
- handleLeavePublicChannel = (callback: () => void) => {
- this.props.actions.leaveChannel(this.props.channel.id);
+const SidebarBaseChannel = ({
+ channel,
+ currentTeamName,
+ actions,
+}: Props) => {
+ const intl = useIntl();
+
+ const handleLeavePublicChannel = useCallback((callback: () => void) => {
+ actions.leaveChannel(channel.id);
trackEvent('ui', 'ui_public_channel_x_button_clicked');
callback();
- };
+ }, [channel.id, actions.leaveChannel]);
- handleLeavePrivateChannel = (callback: () => void) => {
- this.props.actions.openModal({modalId: ModalIdentifiers.LEAVE_PRIVATE_CHANNEL_MODAL, dialogType: LeaveChannelModal, dialogProps: {channel: this.props.channel}});
+ const handleLeavePrivateChannel = useCallback((callback: () => void) => {
+ actions.openModal({modalId: ModalIdentifiers.LEAVE_PRIVATE_CHANNEL_MODAL, dialogType: LeaveChannelModal, dialogProps: {channel}});
trackEvent('ui', 'ui_private_channel_x_button_clicked');
callback();
- };
+ }, [channel, actions.openModal]);
- getChannelLeaveHandler = () => {
- const {channel} = this.props;
-
- if (channel.type === Constants.OPEN_CHANNEL && channel.name !== Constants.DEFAULT_CHANNEL) {
- return this.handleLeavePublicChannel;
- } else if (channel.type === Constants.PRIVATE_CHANNEL) {
- return this.handleLeavePrivateChannel;
- }
-
- return null;
- };
-
- getIcon = () => {
- const {channel} = this.props;
-
- if (channel.shared) {
- return (
-
- );
- } else if (channel.type === Constants.OPEN_CHANNEL) {
- return (
-
- );
- } else if (channel.type === Constants.PRIVATE_CHANNEL) {
- return (
-
- );
- }
-
- return null;
- };
-
- render() {
- const {channel, currentTeamName} = this.props;
-
- let ariaLabelPrefix;
- if (channel.type === Constants.OPEN_CHANNEL) {
- ariaLabelPrefix = localizeMessage('accessibility.sidebar.types.public', 'public channel');
- } else if (channel.type === Constants.PRIVATE_CHANNEL) {
- ariaLabelPrefix = localizeMessage('accessibility.sidebar.types.private', 'private channel');
- }
-
- return (
-
- );
+ let channelLeaveHandler = null;
+ if (channel.type === Constants.OPEN_CHANNEL && channel.name !== Constants.DEFAULT_CHANNEL) {
+ channelLeaveHandler = handleLeavePublicChannel;
+ } else if (channel.type === Constants.PRIVATE_CHANNEL) {
+ channelLeaveHandler = handleLeavePrivateChannel;
}
-}
+
+ const channelIcon = (
+
+ );
+
+ let ariaLabelPrefix;
+ if (channel.type === Constants.OPEN_CHANNEL) {
+ ariaLabelPrefix = intl.formatMessage({id: 'accessibility.sidebar.types.public', defaultMessage: 'public channel'});
+ } else if (channel.type === Constants.PRIVATE_CHANNEL) {
+ ariaLabelPrefix = intl.formatMessage({id: 'accessibility.sidebar.types.private', defaultMessage: 'private channel'});
+ }
+
+ return (
+
+ );
+};
+
+export default SidebarBaseChannel;
diff --git a/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel_icon.tsx b/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel_icon.tsx
new file mode 100644
index 0000000000..96d6a454f1
--- /dev/null
+++ b/webapp/channels/src/components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel_icon.tsx
@@ -0,0 +1,43 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+
+import type {ChannelType} from '@mattermost/types/channels';
+
+import SharedChannelIndicator from 'components/shared_channel_indicator';
+
+import Constants from 'utils/constants';
+
+type Props = {
+ isSharedChannel: boolean;
+ channelType: ChannelType;
+}
+
+const SidebarBaseChannelIcon = ({
+ isSharedChannel,
+ channelType,
+}: Props) => {
+ if (isSharedChannel) {
+ return (
+
+ );
+ }
+ if (channelType === Constants.OPEN_CHANNEL) {
+ return (
+
+ );
+ }
+ if (channelType === Constants.PRIVATE_CHANNEL) {
+ return (
+
+ );
+ }
+ return null;
+};
+
+export default SidebarBaseChannelIcon;