[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
Этот коммит содержится в:
@@ -24,8 +24,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
|
||||
}
|
||||
channelLeaveHandler={[Function]}
|
||||
icon={
|
||||
<i
|
||||
className="icon icon-globe"
|
||||
<SidebarBaseChannelIcon
|
||||
channelType="O"
|
||||
isSharedChannel={false}
|
||||
/>
|
||||
}
|
||||
label="channel_display_name"
|
||||
@@ -57,8 +58,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
|
||||
}
|
||||
channelLeaveHandler={[Function]}
|
||||
icon={
|
||||
<i
|
||||
className="icon icon-lock-outline"
|
||||
<SidebarBaseChannelIcon
|
||||
channelType="P"
|
||||
isSharedChannel={false}
|
||||
/>
|
||||
}
|
||||
label="channel_display_name"
|
||||
@@ -91,10 +93,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
|
||||
}
|
||||
channelLeaveHandler={[Function]}
|
||||
icon={
|
||||
<SharedChannelIndicator
|
||||
<SidebarBaseChannelIcon
|
||||
channelType="O"
|
||||
className="icon"
|
||||
withTooltip={true}
|
||||
isSharedChannel={true}
|
||||
/>
|
||||
}
|
||||
label="channel_display_name"
|
||||
@@ -127,10 +128,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
|
||||
}
|
||||
channelLeaveHandler={[Function]}
|
||||
icon={
|
||||
<SharedChannelIndicator
|
||||
<SidebarBaseChannelIcon
|
||||
channelType="P"
|
||||
className="icon"
|
||||
withTooltip={true}
|
||||
isSharedChannel={true}
|
||||
/>
|
||||
}
|
||||
label="channel_display_name"
|
||||
|
||||
@@ -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<SidebarBaseChannel>(<SidebarBaseChannel {...baseProps}/>);
|
||||
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(<SidebarBaseChannel {...props}/>);
|
||||
|
||||
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<SidebarBaseChannel>(<SidebarBaseChannel {...props}/>);
|
||||
wrapper.instance().handleLeavePrivateChannel(callback);
|
||||
expect(callback).toBeCalled();
|
||||
renderWithContext(<SidebarBaseChannel {...props}/>);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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<Props> {
|
||||
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 (
|
||||
<SharedChannelIndicator
|
||||
className='icon'
|
||||
channelType={channel.type}
|
||||
withTooltip={true}
|
||||
/>
|
||||
);
|
||||
} else if (channel.type === Constants.OPEN_CHANNEL) {
|
||||
return (
|
||||
<i className='icon icon-globe'/>
|
||||
);
|
||||
} else if (channel.type === Constants.PRIVATE_CHANNEL) {
|
||||
return (
|
||||
<i className='icon icon-lock-outline'/>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<SidebarChannelLink
|
||||
channel={channel}
|
||||
link={`/${currentTeamName}/channels/${channel.name}`}
|
||||
label={channel.display_name}
|
||||
ariaLabelPrefix={ariaLabelPrefix}
|
||||
channelLeaveHandler={this.getChannelLeaveHandler()!}
|
||||
icon={this.getIcon()!}
|
||||
/>
|
||||
);
|
||||
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 = (
|
||||
<SidebarBaseChannelIcon
|
||||
isSharedChannel={Boolean(channel.shared)}
|
||||
channelType={channel.type}
|
||||
/>
|
||||
);
|
||||
|
||||
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 (
|
||||
<SidebarChannelLink
|
||||
channel={channel}
|
||||
link={`/${currentTeamName}/channels/${channel.name}`}
|
||||
label={channel.display_name}
|
||||
ariaLabelPrefix={ariaLabelPrefix}
|
||||
channelLeaveHandler={channelLeaveHandler!}
|
||||
icon={channelIcon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarBaseChannel;
|
||||
|
||||
@@ -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 (
|
||||
<SharedChannelIndicator
|
||||
className='icon'
|
||||
channelType={channelType}
|
||||
withTooltip={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (channelType === Constants.OPEN_CHANNEL) {
|
||||
return (
|
||||
<i className='icon icon-globe'/>
|
||||
);
|
||||
}
|
||||
if (channelType === Constants.PRIVATE_CHANNEL) {
|
||||
return (
|
||||
<i className='icon icon-lock-outline'/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default SidebarBaseChannelIcon;
|
||||
Ссылка в новой задаче
Block a user