mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-55278]: Fixed autofocus on submenu's first element (#29547)
This commit is contained in:
parent
169274b3aa
commit
b2b956c043
@ -129,12 +129,10 @@ test('Post actions tab support', async ({pw, axe}) => {
|
||||
// # Press arrow right
|
||||
await channelsPage.postDotMenu.remindMenuItem.press('ArrowRight');
|
||||
|
||||
// * Reminder menu should be visible and have focused
|
||||
channelsPage.postReminderMenu.toBeVisible();
|
||||
await expect(channelsPage.postReminderMenu.container).toBeFocused();
|
||||
// * Reminder menu should be visible
|
||||
expect(channelsPage.postReminderMenu.container).toBeVisible();
|
||||
|
||||
// * Should move focus to 30 mins after arrow down
|
||||
await channelsPage.postReminderMenu.container.press('ArrowDown');
|
||||
// * Should have focus on 30 mins after submenu opens
|
||||
expect(await channelsPage.postReminderMenu.thirtyMinsMenuItem).toBeFocused();
|
||||
|
||||
// * Should move focus to 1 hour after arrow down
|
||||
|
@ -159,13 +159,16 @@ function PostReminderSubmenu(props: Props) {
|
||||
leadingElement={<ClockOutlineIcon size={18}/>}
|
||||
trailingElements={<span className={'dot-menu__item-trailing-icon'}><ChevronRightIcon size={16}/></span>}
|
||||
menuId={`remind_post_${props.post.id}-menu`}
|
||||
subMenuHeader={
|
||||
<h5 className={'dot-menu__post-reminder-menu-header'}>
|
||||
{formatMessage(
|
||||
{
|
||||
id: 'post_info.post_reminder.sub_menu.header',
|
||||
defaultMessage: 'Set a reminder for:',
|
||||
},
|
||||
)}
|
||||
</h5>}
|
||||
>
|
||||
<h5 className={'dot-menu__post-reminder-menu-header'}>
|
||||
{formatMessage(
|
||||
{id: 'post_info.post_reminder.sub_menu.header',
|
||||
defaultMessage: 'Set a reminder for:'},
|
||||
)}
|
||||
</h5>
|
||||
{postReminderSubMenuItems}
|
||||
</Menu.SubMenu>
|
||||
);
|
||||
|
@ -7,12 +7,13 @@
|
||||
min-width: 114px;
|
||||
max-width: 496px;
|
||||
max-height: 80vh;
|
||||
padding: 4px 0;
|
||||
background-color: var(--center-channel-bg);
|
||||
box-shadow: var(--elevation-4);
|
||||
box-shadow: var(--elevation-4), 0 0 0 1px rgba(var(--center-channel-color-rgb), 0.12) inset;
|
||||
}
|
||||
&.AsSubMenu {
|
||||
& .MuiPaper-root {
|
||||
box-shadow: var(--elevation-5);
|
||||
box-shadow: var(--elevation-5), 0 0 0 1px rgba(var(--center-channel-color-rgb), 0.12) inset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ describe('menu click handlers', () => {
|
||||
expect(screen.getByText('Open modal from submenu')).toBeInTheDocument();
|
||||
|
||||
// Press the down arrow once to focus first submenu item and then twice more to select the one we want
|
||||
userEvent.keyboard('{arrowdown}{arrowdown}{arrowdown}');
|
||||
userEvent.keyboard('{arrowdown}{arrowdown}');
|
||||
|
||||
expect(screen.getByText('Open modal from submenu').closest('li')).toHaveFocus();
|
||||
|
||||
|
@ -309,6 +309,7 @@ export const MenuItemStyled = styled(MuiMenuItem, {
|
||||
flexWrap: 'nowrap',
|
||||
justifyContent: 'flex-end',
|
||||
color: isRegular ? 'rgba(var(--center-channel-color-rgb), 0.75)' : 'var(--error-text)',
|
||||
marginInlineStart: '24px',
|
||||
gap: '4px',
|
||||
fontSize: '12px',
|
||||
lineHeight: '16px',
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import MuiMenu from '@mui/material/Menu';
|
||||
import MuiMenuList from '@mui/material/MenuList';
|
||||
import MuiPopover from '@mui/material/Popover';
|
||||
import type {PopoverOrigin} from '@mui/material/Popover';
|
||||
import React, {
|
||||
useState,
|
||||
@ -33,6 +33,8 @@ import {SubMenuContext, useMenuContextValue} from './menu_context';
|
||||
import {MenuItem} from './menu_item';
|
||||
import type {Props as MenuItemProps} from './menu_item';
|
||||
|
||||
import './menu.scss';
|
||||
|
||||
interface Props {
|
||||
id: MenuItemProps['id'];
|
||||
leadingElement?: MenuItemProps['leadingElement'];
|
||||
@ -47,6 +49,7 @@ interface Props {
|
||||
menuAriaDescribedBy?: string;
|
||||
forceOpenOnLeft?: boolean; // Most of the times this is not needed, since submenu position is calculated and placed
|
||||
|
||||
subMenuHeader?: ReactNode;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
@ -63,6 +66,7 @@ export function SubMenu(props: Props) {
|
||||
menuAriaDescribedBy,
|
||||
forceOpenOnLeft,
|
||||
children,
|
||||
subMenuHeader,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@ -136,6 +140,7 @@ export function SubMenu(props: Props) {
|
||||
menuId,
|
||||
menuAriaLabel,
|
||||
children,
|
||||
subMenuHeader,
|
||||
},
|
||||
}));
|
||||
}
|
||||
@ -166,35 +171,30 @@ export function SubMenu(props: Props) {
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<MuiMenu
|
||||
anchorEl={anchorElement}
|
||||
open={isSubMenuOpen}
|
||||
anchorOrigin={originOfAnchorAndTransform.anchorOrigin}
|
||||
transformOrigin={originOfAnchorAndTransform.transformOrigin}
|
||||
sx={{pointerEvents: 'none'}}
|
||||
className='menu_menuStyled AsSubMenu'
|
||||
>
|
||||
{/* This component is needed here to re enable pointer events for the submenu items which we had to disable above as */}
|
||||
{/* pointer turns to default as soon as it leaves the parent menu */}
|
||||
{/* Notice we dont use the below component in menu.tsx */}
|
||||
<MuiMenuList
|
||||
id={menuId}
|
||||
component='ul'
|
||||
aria-label={menuAriaLabel}
|
||||
aria-describedby={menuAriaDescribedBy}
|
||||
className={A11yClassNames.POPUP}
|
||||
onKeyDown={handleSubMenuKeyDown}
|
||||
sx={{
|
||||
pointerEvents: 'auto', // reset pointer events to default from here on
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
}}
|
||||
<SubMenuContext.Provider value={providerValue}>
|
||||
<MuiPopover
|
||||
anchorEl={anchorElement}
|
||||
open={isSubMenuOpen}
|
||||
anchorOrigin={originOfAnchorAndTransform.anchorOrigin}
|
||||
transformOrigin={originOfAnchorAndTransform.transformOrigin}
|
||||
className='menu_menuStyled AsSubMenu'
|
||||
>
|
||||
<SubMenuContext.Provider value={providerValue}>
|
||||
{subMenuHeader}
|
||||
<MuiMenuList
|
||||
id={menuId}
|
||||
aria-label={menuAriaLabel}
|
||||
aria-describedby={menuAriaDescribedBy}
|
||||
className={A11yClassNames.POPUP}
|
||||
onKeyDown={handleSubMenuKeyDown}
|
||||
autoFocusItem={isSubMenuOpen}
|
||||
sx={{
|
||||
py: 0,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SubMenuContext.Provider>
|
||||
</MuiMenuList>
|
||||
</MuiMenu>
|
||||
</MuiMenuList>
|
||||
</MuiPopover>
|
||||
</SubMenuContext.Provider>
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
@ -203,6 +203,7 @@ interface SubMenuModalProps {
|
||||
menuId: Props['menuId'];
|
||||
menuAriaLabel?: Props['menuAriaLabel'];
|
||||
children: Props['children'];
|
||||
subMenuHeader?: ReactNode;
|
||||
}
|
||||
|
||||
function SubMenuModal(props: SubMenuModalProps) {
|
||||
@ -224,9 +225,11 @@ function SubMenuModal(props: SubMenuModalProps) {
|
||||
className='menuModal'
|
||||
>
|
||||
<MuiMenuList
|
||||
component={'div'}
|
||||
aria-hidden={true}
|
||||
onClick={handleModalClose}
|
||||
>
|
||||
{props.subMenuHeader}
|
||||
{props.children}
|
||||
</MuiMenuList>
|
||||
</GenericModal>
|
||||
|
@ -4,7 +4,7 @@ exports[`UserAccountNameMenuItem should not break if no props are passed 1`] = `
|
||||
<div>
|
||||
<li
|
||||
aria-haspopup="true"
|
||||
class="MuiButtonBase-root-JvZdr dKFJFs MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXqYNm kIRdVO MuiMenuItem-root MuiMenuItem-gutters sc-gswNZR cUFZeQ userAccountMenu_nameMenuItem"
|
||||
class="MuiButtonBase-root-JvZdr dKFJFs MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiMenuItem-root-dXqYNm kIRdVO MuiMenuItem-root MuiMenuItem-gutters sc-gswNZR jjvBbU userAccountMenu_nameMenuItem"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
|
@ -196,20 +196,21 @@ export default function UserAccountDndMenuItem(props: Props) {
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
role='menuitemradio' // Prevents menu item from closing, not a recommended solution
|
||||
aria-checked={props.isStatusDnd}
|
||||
trailingElements={trailingElement}
|
||||
subMenuHeader={
|
||||
<h5
|
||||
id='userAccountMenu_dndSubMenuTitle'
|
||||
className='userAccountMenu_dndMenuItem_subMenuTitle'
|
||||
aria-hidden={true}
|
||||
>
|
||||
{formatMessage({
|
||||
id: 'userAccountMenu.dndSubMenu.title',
|
||||
defaultMessage: 'Clear after:',
|
||||
})}
|
||||
</h5>
|
||||
}
|
||||
>
|
||||
<h5
|
||||
id='userAccountMenu_dndSubMenuTitle'
|
||||
className='userAccountMenu_dndMenuItem_subMenuTitle'
|
||||
aria-hidden={true}
|
||||
>
|
||||
{formatMessage({
|
||||
id: 'userAccountMenu.dndSubMenu.title',
|
||||
defaultMessage: 'Clear after:',
|
||||
})}
|
||||
</h5>
|
||||
<Menu.Item
|
||||
id={DND_SUB_MENU_ITEMS_IDS.DO_NOT_CLEAR}
|
||||
labels={
|
||||
|
Loading…
Reference in New Issue
Block a user