mirror of
https://github.com/mattermost/mattermost.git
synced 2026-09-21 05:54:10 +08:00
[MM-68266] Pass through menu props to popout menu item, guard at menu definition to avoid null component blocking keyboard navigation (#36024)
* [MM-68266] Pass through menu props to popout menu item, guard at menu definition to avoid null component blocking keyboard navigation * fix tests * Update webapp/channels/src/components/sidebar/sidebar_channel/sidebar_channel_menu/sidebar_channel_menu.test.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
coderabbitai[bot]
parent
e3b2b0a521
commit
2fb38fe71d
@@ -28,6 +28,7 @@ import {getIsChannelBookmarksEnabled} from 'components/channel_bookmarks/utils';
|
||||
import * as Menu from 'components/menu';
|
||||
|
||||
import {Constants} from 'utils/constants';
|
||||
import {canPopout, isChannelPopoutWindow} from 'utils/popouts/popout_windows';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
@@ -149,7 +150,9 @@ export default function ChannelHeaderMenu({dmUser, gmMembers, isMobile, archived
|
||||
horizontal: 'left',
|
||||
}}
|
||||
>
|
||||
<MenuItemOpenInNewWindow channel={channel}/>
|
||||
{canPopout() && !isChannelPopoutWindow() && (
|
||||
<MenuItemOpenInNewWindow channel={channel}/>
|
||||
)}
|
||||
{isDirect && (
|
||||
<ChannelDirectMenu
|
||||
channel={channel}
|
||||
|
||||
+5
-6
@@ -8,7 +8,7 @@ import type {ChannelType} from '@mattermost/types/channels';
|
||||
import {WithTestMenuContext} from 'components/menu/menu_context_test';
|
||||
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
import {isChannelPopoutWindow, popoutChannel} from 'utils/popouts/popout_windows';
|
||||
import {popoutChannel} from 'utils/popouts/popout_windows';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
import MenuItemOpenInNewWindow from './open_in_new_window';
|
||||
@@ -54,21 +54,20 @@ describe('MenuItemOpenInNewWindow', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.mocked(isChannelPopoutWindow).mockReturnValue(false);
|
||||
});
|
||||
|
||||
test('should render nothing when already in a channel popout', () => {
|
||||
jest.mocked(isChannelPopoutWindow).mockReturnValue(true);
|
||||
test('should render menu item and separator', () => {
|
||||
const channel = TestHelper.getChannelMock({type: 'O' as ChannelType, name: 'town-square'});
|
||||
|
||||
const {container} = renderWithContext(
|
||||
renderWithContext(
|
||||
<WithTestMenuContext>
|
||||
<MenuItemOpenInNewWindow channel={channel}/>
|
||||
</WithTestMenuContext>,
|
||||
baseState,
|
||||
);
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
expect(screen.getByText('Open in new window')).toBeInTheDocument();
|
||||
expect(screen.getByRole('separator')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should call popoutChannel when clicked', async () => {
|
||||
|
||||
+5
-8
@@ -13,19 +13,19 @@ import {getUserIdFromChannelName} from 'mattermost-redux/utils/channel_utils';
|
||||
|
||||
import {getPopoutChannelTitle} from 'components/channel_popout/channel_popout';
|
||||
import * as Menu from 'components/menu';
|
||||
import PopoutMenuItem from 'components/popout_menu_item';
|
||||
import PopoutMenuItem, {type PopoutMenuItemProps} from 'components/popout_menu_item';
|
||||
|
||||
import {getChannelRoutePathAndIdentifier} from 'utils/channel_utils';
|
||||
import {Constants} from 'utils/constants';
|
||||
import {isChannelPopoutWindow, popoutChannel} from 'utils/popouts/popout_windows';
|
||||
import {popoutChannel} from 'utils/popouts/popout_windows';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
interface Props {
|
||||
interface Props extends PopoutMenuItemProps {
|
||||
channel: Channel;
|
||||
}
|
||||
|
||||
const MenuItemOpenInNewWindow = ({channel}: Props) => {
|
||||
const MenuItemOpenInNewWindow = ({channel, ...rest}: Props) => {
|
||||
const intl = useIntl();
|
||||
const team = useSelector(getCurrentTeam);
|
||||
const currentUserId = useSelector(getCurrentUserId);
|
||||
@@ -37,10 +37,6 @@ const MenuItemOpenInNewWindow = ({channel}: Props) => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (isChannelPopoutWindow()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
if (!team) {
|
||||
return;
|
||||
@@ -55,6 +51,7 @@ const MenuItemOpenInNewWindow = ({channel}: Props) => {
|
||||
<PopoutMenuItem
|
||||
id='channelOpenInNewWindow'
|
||||
onClick={handleClick}
|
||||
{...rest}
|
||||
/>
|
||||
<Menu.Separator/>
|
||||
</>
|
||||
|
||||
@@ -7,15 +7,13 @@ import {FormattedMessage} from 'react-intl';
|
||||
import {DockWindowIcon} from '@mattermost/compass-icons/components';
|
||||
|
||||
import * as Menu from 'components/menu';
|
||||
import type {Props as MenuItemProps} from 'components/menu/menu_item';
|
||||
|
||||
import {canPopout} from 'utils/popouts/popout_windows';
|
||||
|
||||
type Props = {
|
||||
onClick: () => void;
|
||||
id?: string;
|
||||
};
|
||||
export type PopoutMenuItemProps = Omit<MenuItemProps, 'labels' | 'leadingElement'>;
|
||||
|
||||
export default function PopoutMenuItem({onClick, id = 'openInNewWindow'}: Props) {
|
||||
export default function PopoutMenuItem({onClick, id = 'openInNewWindow', ...rest}: PopoutMenuItemProps) {
|
||||
if (!canPopout()) {
|
||||
return null;
|
||||
}
|
||||
@@ -31,6 +29,7 @@ export default function PopoutMenuItem({onClick, id = 'openInNewWindow'}: Props)
|
||||
/>
|
||||
}
|
||||
onClick={onClick}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+20
-20
@@ -108,10 +108,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should match sn
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
@@ -449,10 +449,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should match sn
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
@@ -790,10 +790,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should match sn
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
@@ -1131,10 +1131,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
@@ -1404,10 +1404,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
@@ -1713,10 +1713,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
@@ -2051,10 +2051,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
@@ -2392,10 +2392,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
@@ -2733,10 +2733,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
@@ -3074,10 +3074,10 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
class="MuiButtonBase-root-JDVeC cxEgXn MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-focusVisible MuiMenuItem-root-dXjcMb iLWjgG MuiMenuItem-root MuiMenuItem-gutters sc-grYavY jmUrfe"
|
||||
id="channelOpenInNewWindow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="leading-element"
|
||||
|
||||
+19
@@ -9,6 +9,7 @@ import {CategoryTypes} from 'mattermost-redux/constants/channel_categories';
|
||||
|
||||
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
|
||||
import Constants from 'utils/constants';
|
||||
import {canPopout, isChannelPopoutWindow} from 'utils/popouts/popout_windows';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
import SidebarChannelMenu from './sidebar_channel_menu';
|
||||
@@ -22,6 +23,12 @@ jest.mock('react-intl', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('utils/popouts/popout_windows', () => ({
|
||||
...jest.requireActual('utils/popouts/popout_windows'),
|
||||
canPopout: jest.fn(() => true),
|
||||
isChannelPopoutWindow: jest.fn(() => false),
|
||||
}));
|
||||
|
||||
describe('components/sidebar/sidebar_channel/sidebar_channel_menu', () => {
|
||||
const testChannel = TestHelper.getChannelMock();
|
||||
const testCategory = TestHelper.getCategoryMock();
|
||||
@@ -260,4 +267,16 @@ describe('components/sidebar/sidebar_channel/sidebar_channel_menu', () => {
|
||||
await openMenu();
|
||||
expect(document.body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not show Open in new window when popout is not available', async () => {
|
||||
jest.mocked(canPopout).mockReturnValue(false);
|
||||
jest.mocked(isChannelPopoutWindow).mockReturnValue(false);
|
||||
|
||||
renderWithContext(
|
||||
<SidebarChannelMenu {...baseProps}/>,
|
||||
);
|
||||
|
||||
await openMenu();
|
||||
expect(screen.queryByRole('menuitem', {name: 'Open in new window'})).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
+4
-1
@@ -22,6 +22,7 @@ import ChannelMoveToSubmenu from 'components/channel_move_to_sub_menu';
|
||||
import * as Menu from 'components/menu';
|
||||
|
||||
import Constants, {ModalIdentifiers} from 'utils/constants';
|
||||
import {canPopout, isChannelPopoutWindow} from 'utils/popouts/popout_windows';
|
||||
import {copyToClipboard} from 'utils/utils';
|
||||
|
||||
import type {PropsFromRedux, OwnProps} from './index';
|
||||
@@ -299,7 +300,9 @@ const SidebarChannelMenu = ({
|
||||
onToggle: onMenuToggle,
|
||||
}}
|
||||
>
|
||||
<MenuItemOpenInNewWindow channel={channel}/>
|
||||
{canPopout() && !isChannelPopoutWindow() && (
|
||||
<MenuItemOpenInNewWindow channel={channel}/>
|
||||
)}
|
||||
{markAsReadUnreadMenuItem}
|
||||
{favoriteUnfavoriteMenuItem}
|
||||
{muteUnmuteChannelMenuItem}
|
||||
|
||||
Reference in New Issue
Block a user