MM-62621 - a11y fixes channel info bottom menu (#29910)

* MM-62621 - a11y fixes channel info bottom menu

* fix translations

* add back library import style

* remove unnecesary pseudo menu aria roles and turn into plain buttons

* fix translations

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Pablo Vélez
2025-01-30 07:39:22 -05:00
коммит произвёл GitHub
родитель 8c3490b3d8
Коммит dc06bb2155
3 изменённых файлов: 81 добавлений и 78 удалений

Просмотреть файл

@@ -42,19 +42,15 @@ export interface Props {
channelStats: ChannelStats; channelStats: ChannelStats;
currentUser: UserProfile; currentUser: UserProfile;
currentTeam: Team; currentTeam: Team;
isArchived: boolean; isArchived: boolean;
isFavorite: boolean; isFavorite: boolean;
isMuted: boolean; isMuted: boolean;
isInvitingPeople: boolean; isInvitingPeople: boolean;
isMobile: boolean; isMobile: boolean;
canManageMembers: boolean; canManageMembers: boolean;
canManageProperties: boolean; canManageProperties: boolean;
dmUser?: DMUser; dmUser?: DMUser;
channelMembers: UserProfile[]; channelMembers: UserProfile[];
actions: { actions: {
closeRightHandSide: () => void; closeRightHandSide: () => void;
unfavoriteChannel: (channelId: string) => void; unfavoriteChannel: (channelId: string) => void;
@@ -155,36 +151,26 @@ const ChannelInfoRhs = ({
isMobile={isMobile} isMobile={isMobile}
onClose={actions.closeRightHandSide} onClose={actions.closeRightHandSide}
/> />
<TopButtons <TopButtons
channelType={channel.type} channelType={channel.type}
channelURL={channelURL} channelURL={channelURL}
isFavorite={isFavorite} isFavorite={isFavorite}
isMuted={isMuted} isMuted={isMuted}
isInvitingPeople={isInvitingPeople} isInvitingPeople={isInvitingPeople}
canAddPeople={canManageMembers} canAddPeople={canManageMembers}
actions={{toggleFavorite, toggleMute, addPeople}} actions={{toggleFavorite, toggleMute, addPeople}}
/> />
<AboutArea <AboutArea
channel={channel} channel={channel}
dmUser={dmUser} dmUser={dmUser}
gmUsers={gmUsers} gmUsers={gmUsers}
canEditChannelProperties={canEditChannelProperties} canEditChannelProperties={canEditChannelProperties}
actions={{ actions={{
editChannelHeader, editChannelHeader,
editChannelPurpose, editChannelPurpose,
}} }}
/> />
<Divider/> <Divider/>
<Menu <Menu
channel={channel} channel={channel}
channelStats={channelStats} channelStats={channelStats}

Просмотреть файл

@@ -11,10 +11,33 @@ import LoadingSpinner from 'components/widgets/loading/loading_spinner';
import {Constants} from 'utils/constants'; import {Constants} from 'utils/constants';
const MenuItemContainer = styled.div` const MenuContainer = styled.nav`
padding: 8px 16px;
flex: 1;
display: flex; display: flex;
flex-direction: column;
align-items: flex-start;
padding: 16px 0;
font-size: 14px;
line-height: 20px;
color: rgb(var(--center-channel-color-rgb));
`;
const MenuItemButton = styled.button`
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
height: 40px;
padding: 8px 16px;
background: none;
border: none;
text-align: left;
cursor: pointer;
&:hover {
background: rgba(var(--center-channel-color-rgb), 0.08);
}
`; `;
const Icon = styled.div` const Icon = styled.div`
@@ -40,56 +63,38 @@ const Badge = styled.div`
`; `;
interface MenuItemProps { interface MenuItemProps {
className?: string;
icon: JSX.Element; icon: JSX.Element;
text: string; text: string;
opensSubpanel?: boolean; opensSubpanel?: boolean;
badge?: string|number|JSX.Element; badge?: string | number | JSX.Element;
onClick: () => void; onClick: () => void;
} }
const menuItem = ({icon, text, className, opensSubpanel, badge, onClick}: MenuItemProps) => { function MenuItem(props: MenuItemProps) {
const {icon, text, opensSubpanel, badge, onClick} = props;
const hasRightSide = (badge !== undefined) || opensSubpanel; const hasRightSide = (badge !== undefined) || opensSubpanel;
return ( return (
<div className={className}> <MenuItemButton
<MenuItemContainer onClick={onClick}> onClick={onClick}
<Icon>{icon}</Icon> aria-label={text}
<MenuItemText> type='button'
{text} >
</MenuItemText> <Icon>{icon}</Icon>
<MenuItemText>{text}</MenuItemText>
{hasRightSide && ( {hasRightSide && (
<RightSide> <RightSide>
{badge !== undefined && ( {badge !== undefined && (
<Badge>{badge}</Badge> <Badge>{badge}</Badge>
)} )}
{opensSubpanel && ( {opensSubpanel && (
<Icon><i className='icon icon-chevron-right'/></Icon> <Icon><i className='icon icon-chevron-right'/></Icon>
)} )}
</RightSide> </RightSide>
)} )}
</MenuItemContainer> </MenuItemButton>
</div>
); );
}; }
const MenuItem = styled(menuItem)`
display: flex;
width: 100%;
height: 40px;
flex-direction: row;
align-items: center;
cursor: pointer;
&:hover {
background: rgba(var(--center-channel-color-rgb), 0.08);
${Icon} {
color: rgba(var(--center-channel-color-rgb), var(--icon-opacity-hover));
}
}
`;
interface MenuProps { interface MenuProps {
channel: Channel; channel: Channel;
@@ -107,8 +112,16 @@ interface MenuProps {
}; };
} }
const Menu = ({channel, channelStats, isArchived, className, actions}: MenuProps) => { export default function Menu(props: MenuProps) {
const {formatMessage} = useIntl(); const {formatMessage} = useIntl();
const {
channel,
channelStats,
isArchived,
className,
actions,
} = props;
const [loadingStats, setLoadingStats] = useState(true); const [loadingStats, setLoadingStats] = useState(true);
const showNotificationPreferences = channel.type !== Constants.DM_CHANNEL && !isArchived; const showNotificationPreferences = channel.type !== Constants.DM_CHANNEL && !isArchived;
@@ -125,21 +138,31 @@ const Menu = ({channel, channelStats, isArchived, className, actions}: MenuProps
}, [channel.id]); }, [channel.id]);
return ( return (
<div <MenuContainer
className={className} className={className}
data-testid='channel_info_rhs-menu' data-testid='channel_info_rhs-menu'
aria-label={formatMessage({
id: 'channel_info_rhs.menu.title',
defaultMessage: 'Channel Info Actions',
})}
> >
{showNotificationPreferences && ( {showNotificationPreferences && (
<MenuItem <MenuItem
icon={<i className='icon icon-bell-outline'/>} icon={<i className='icon icon-bell-outline'/>}
text={formatMessage({id: 'channel_info_rhs.menu.notification_preferences', defaultMessage: 'Notification Preferences'})} text={formatMessage({
id: 'channel_info_rhs.menu.notification_preferences',
defaultMessage: 'Notification Preferences',
})}
onClick={actions.openNotificationSettings} onClick={actions.openNotificationSettings}
/> />
)} )}
{showMembers && ( {showMembers && (
<MenuItem <MenuItem
icon={<i className='icon icon-account-outline'/>} icon={<i className='icon icon-account-outline'/>}
text={formatMessage({id: 'channel_info_rhs.menu.members', defaultMessage: 'Members'})} text={formatMessage({
id: 'channel_info_rhs.menu.members',
defaultMessage: 'Members',
})}
opensSubpanel={true} opensSubpanel={true}
badge={channelStats.member_count} badge={channelStats.member_count}
onClick={() => actions.showChannelMembers(channel.id)} onClick={() => actions.showChannelMembers(channel.id)}
@@ -147,31 +170,24 @@ const Menu = ({channel, channelStats, isArchived, className, actions}: MenuProps
)} )}
<MenuItem <MenuItem
icon={<i className='icon icon-pin-outline'/>} icon={<i className='icon icon-pin-outline'/>}
text={formatMessage({id: 'channel_info_rhs.menu.pinned', defaultMessage: 'Pinned messages'})} text={formatMessage({
id: 'channel_info_rhs.menu.pinned',
defaultMessage: 'Pinned messages',
})}
opensSubpanel={true} opensSubpanel={true}
badge={channelStats?.pinnedpost_count} badge={channelStats?.pinnedpost_count}
onClick={() => actions.showPinnedPosts(channel.id)} onClick={() => actions.showPinnedPosts(channel.id)}
/> />
<MenuItem <MenuItem
icon={<i className='icon icon-file-text-outline'/>} icon={<i className='icon icon-file-text-outline'/>}
text={formatMessage({id: 'channel_info_rhs.menu.files', defaultMessage: 'Files'})} text={formatMessage({
id: 'channel_info_rhs.menu.files',
defaultMessage: 'Files',
})}
opensSubpanel={true} opensSubpanel={true}
badge={loadingStats ? <LoadingSpinner/> : fileCount} badge={loadingStats ? <LoadingSpinner/> : fileCount}
onClick={() => actions.showChannelFiles(channel.id)} onClick={() => actions.showChannelFiles(channel.id)}
/> />
</div> </MenuContainer>
); );
}; }
const StyledMenu = styled(Menu)`
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 16px 0;
font-size: 14px;
line-height: 20px;
color: rgb(var(--center-channel-color-rgb));
`;
export default StyledMenu;

Просмотреть файл

@@ -3261,6 +3261,7 @@
"channel_info_rhs.menu.members": "Members", "channel_info_rhs.menu.members": "Members",
"channel_info_rhs.menu.notification_preferences": "Notification Preferences", "channel_info_rhs.menu.notification_preferences": "Notification Preferences",
"channel_info_rhs.menu.pinned": "Pinned messages", "channel_info_rhs.menu.pinned": "Pinned messages",
"channel_info_rhs.menu.title": "Channel Info Actions",
"channel_info_rhs.top_buttons.add_people": "Add People", "channel_info_rhs.top_buttons.add_people": "Add People",
"channel_info_rhs.top_buttons.add_people.tooltip": "Add team members to this channel", "channel_info_rhs.top_buttons.add_people.tooltip": "Add team members to this channel",
"channel_info_rhs.top_buttons.copied": "Copied", "channel_info_rhs.top_buttons.copied": "Copied",