[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]}
|
channelLeaveHandler={[Function]}
|
||||||
icon={
|
icon={
|
||||||
<i
|
<SidebarBaseChannelIcon
|
||||||
className="icon icon-globe"
|
channelType="O"
|
||||||
|
isSharedChannel={false}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="channel_display_name"
|
label="channel_display_name"
|
||||||
@@ -57,8 +58,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
|
|||||||
}
|
}
|
||||||
channelLeaveHandler={[Function]}
|
channelLeaveHandler={[Function]}
|
||||||
icon={
|
icon={
|
||||||
<i
|
<SidebarBaseChannelIcon
|
||||||
className="icon icon-lock-outline"
|
channelType="P"
|
||||||
|
isSharedChannel={false}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="channel_display_name"
|
label="channel_display_name"
|
||||||
@@ -91,10 +93,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
|
|||||||
}
|
}
|
||||||
channelLeaveHandler={[Function]}
|
channelLeaveHandler={[Function]}
|
||||||
icon={
|
icon={
|
||||||
<SharedChannelIndicator
|
<SidebarBaseChannelIcon
|
||||||
channelType="O"
|
channelType="O"
|
||||||
className="icon"
|
isSharedChannel={true}
|
||||||
withTooltip={true}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="channel_display_name"
|
label="channel_display_name"
|
||||||
@@ -127,10 +128,9 @@ exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match sn
|
|||||||
}
|
}
|
||||||
channelLeaveHandler={[Function]}
|
channelLeaveHandler={[Function]}
|
||||||
icon={
|
icon={
|
||||||
<SharedChannelIndicator
|
<SidebarBaseChannelIcon
|
||||||
channelType="P"
|
channelType="P"
|
||||||
className="icon"
|
isSharedChannel={true}
|
||||||
withTooltip={true}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="channel_display_name"
|
label="channel_display_name"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {screen, waitFor} from '@testing-library/react';
|
||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import React from 'react';
|
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 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', () => {
|
describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
|
||||||
const baseProps = {
|
const baseProps = {
|
||||||
channel: {
|
channel: {
|
||||||
@@ -91,25 +94,64 @@ describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
|
|||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('expect callback to be called when leave public channel ', () => {
|
test('expect leaveChannel to be called when leave public channel ', async () => {
|
||||||
const callback = jest.fn();
|
const mockfn = jest.fn();
|
||||||
const wrapper = shallow<SidebarBaseChannel>(<SidebarBaseChannel {...baseProps}/>);
|
|
||||||
wrapper.instance().handleLeavePublicChannel(callback);
|
const props = {
|
||||||
expect(callback).toBeCalled();
|
...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 ', () => {
|
test('expect openModal to be called when leave private channel ', async () => {
|
||||||
const callback = jest.fn();
|
const mockfn = jest.fn();
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
...baseProps,
|
...baseProps,
|
||||||
channel: {
|
channel: {
|
||||||
...baseProps.channel,
|
...baseProps.channel,
|
||||||
type: 'P' as ChannelType,
|
type: 'P' as ChannelType,
|
||||||
|
name: 'l',
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
leaveChannel: jest.fn(),
|
||||||
|
openModal: mockfn,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallow<SidebarBaseChannel>(<SidebarBaseChannel {...props}/>);
|
renderWithContext(<SidebarBaseChannel {...props}/>);
|
||||||
wrapper.instance().handleLeavePrivateChannel(callback);
|
|
||||||
expect(callback).toBeCalled();
|
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.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// 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 type {Channel} from '@mattermost/types/channels';
|
||||||
|
|
||||||
import {trackEvent} from 'actions/telemetry_actions';
|
import {trackEvent} from 'actions/telemetry_actions';
|
||||||
|
|
||||||
import LeaveChannelModal from 'components/leave_channel_modal';
|
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 SidebarChannelLink from 'components/sidebar/sidebar_channel/sidebar_channel_link';
|
||||||
|
|
||||||
import Constants, {ModalIdentifiers} from 'utils/constants';
|
import Constants, {ModalIdentifiers} from 'utils/constants';
|
||||||
import {localizeMessage} from 'utils/utils';
|
|
||||||
|
import SidebarBaseChannelIcon from './sidebar_base_channel_icon';
|
||||||
|
|
||||||
import type {PropsFromRedux} from './index';
|
import type {PropsFromRedux} from './index';
|
||||||
|
|
||||||
@@ -21,74 +22,56 @@ export interface Props extends PropsFromRedux {
|
|||||||
currentTeamName: string;
|
currentTeamName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SidebarBaseChannel extends React.PureComponent<Props> {
|
const SidebarBaseChannel = ({
|
||||||
handleLeavePublicChannel = (callback: () => void) => {
|
channel,
|
||||||
this.props.actions.leaveChannel(this.props.channel.id);
|
currentTeamName,
|
||||||
|
actions,
|
||||||
|
}: Props) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const handleLeavePublicChannel = useCallback((callback: () => void) => {
|
||||||
|
actions.leaveChannel(channel.id);
|
||||||
trackEvent('ui', 'ui_public_channel_x_button_clicked');
|
trackEvent('ui', 'ui_public_channel_x_button_clicked');
|
||||||
callback();
|
callback();
|
||||||
};
|
}, [channel.id, actions.leaveChannel]);
|
||||||
|
|
||||||
handleLeavePrivateChannel = (callback: () => void) => {
|
const handleLeavePrivateChannel = useCallback((callback: () => void) => {
|
||||||
this.props.actions.openModal({modalId: ModalIdentifiers.LEAVE_PRIVATE_CHANNEL_MODAL, dialogType: LeaveChannelModal, dialogProps: {channel: this.props.channel}});
|
actions.openModal({modalId: ModalIdentifiers.LEAVE_PRIVATE_CHANNEL_MODAL, dialogType: LeaveChannelModal, dialogProps: {channel}});
|
||||||
trackEvent('ui', 'ui_private_channel_x_button_clicked');
|
trackEvent('ui', 'ui_private_channel_x_button_clicked');
|
||||||
callback();
|
callback();
|
||||||
};
|
}, [channel, actions.openModal]);
|
||||||
|
|
||||||
getChannelLeaveHandler = () => {
|
let channelLeaveHandler = null;
|
||||||
const {channel} = this.props;
|
if (channel.type === Constants.OPEN_CHANNEL && channel.name !== Constants.DEFAULT_CHANNEL) {
|
||||||
|
channelLeaveHandler = handleLeavePublicChannel;
|
||||||
if (channel.type === Constants.OPEN_CHANNEL && channel.name !== Constants.DEFAULT_CHANNEL) {
|
} else if (channel.type === Constants.PRIVATE_CHANNEL) {
|
||||||
return this.handleLeavePublicChannel;
|
channelLeaveHandler = handleLeavePrivateChannel;
|
||||||
} 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()!}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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