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>
Этот коммит содержится в:
@@ -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",
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user