[MM-55270] fix(accessibility): "channels" button in global header is not selectable with keyboard (#29224)
Этот коммит содержится в:
@@ -11,12 +11,22 @@ exports[`components/global/product_switcher should have an active button state w
|
||||
onClick={[Function]}
|
||||
>
|
||||
<ProductMenuButton
|
||||
active={true}
|
||||
aria-controls="product-switcher-menu"
|
||||
aria-expanded={true}
|
||||
aria-label="Product switch menu"
|
||||
/>
|
||||
<ProductBranding />
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(var(--sidebar-text-rgb), 0.16)",
|
||||
"color": "rgba(var(--sidebar-text-rgb), 0.56)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ProductsIcon
|
||||
color="rgba(var(--sidebar-text-rgb), 0.56)"
|
||||
size={20}
|
||||
/>
|
||||
<ProductBranding />
|
||||
</ProductMenuButton>
|
||||
</ProductMenuContainer>
|
||||
<Menu
|
||||
ariaLabel="switcherOpen"
|
||||
@@ -75,12 +85,22 @@ exports[`components/global/product_switcher should match snapshot 1`] = `
|
||||
onClick={[Function]}
|
||||
>
|
||||
<ProductMenuButton
|
||||
active={true}
|
||||
aria-controls="product-switcher-menu"
|
||||
aria-expanded={true}
|
||||
aria-label="Product switch menu"
|
||||
/>
|
||||
<ProductBranding />
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(var(--sidebar-text-rgb), 0.16)",
|
||||
"color": "rgba(var(--sidebar-text-rgb), 0.56)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ProductsIcon
|
||||
color="rgba(var(--sidebar-text-rgb), 0.56)"
|
||||
size={20}
|
||||
/>
|
||||
<ProductBranding />
|
||||
</ProductMenuButton>
|
||||
</ProductMenuContainer>
|
||||
<Menu
|
||||
ariaLabel="switcherOpen"
|
||||
@@ -139,12 +159,22 @@ exports[`components/global/product_switcher should match snapshot with product s
|
||||
onClick={[Function]}
|
||||
>
|
||||
<ProductMenuButton
|
||||
active={true}
|
||||
aria-controls="product-switcher-menu"
|
||||
aria-expanded={true}
|
||||
aria-label="Product switch menu"
|
||||
/>
|
||||
<ProductBranding />
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(var(--sidebar-text-rgb), 0.16)",
|
||||
"color": "rgba(var(--sidebar-text-rgb), 0.56)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ProductsIcon
|
||||
color="rgba(var(--sidebar-text-rgb), 0.56)"
|
||||
size={20}
|
||||
/>
|
||||
<ProductBranding />
|
||||
</ProductMenuButton>
|
||||
</ProductMenuContainer>
|
||||
<Menu
|
||||
ariaLabel="switcherOpen"
|
||||
@@ -203,12 +233,22 @@ exports[`components/global/product_switcher should match snapshot without licens
|
||||
onClick={[Function]}
|
||||
>
|
||||
<ProductMenuButton
|
||||
active={true}
|
||||
aria-controls="product-switcher-menu"
|
||||
aria-expanded={true}
|
||||
aria-label="Product switch menu"
|
||||
/>
|
||||
<ProductBrandingTeamEdition />
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(var(--sidebar-text-rgb), 0.16)",
|
||||
"color": "rgba(var(--sidebar-text-rgb), 0.56)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ProductsIcon
|
||||
color="rgba(var(--sidebar-text-rgb), 0.56)"
|
||||
size={20}
|
||||
/>
|
||||
<ProductBrandingTeamEdition />
|
||||
</ProductMenuButton>
|
||||
</ProductMenuContainer>
|
||||
<Menu
|
||||
ariaLabel="switcherOpen"
|
||||
@@ -267,12 +307,22 @@ exports[`components/global/product_switcher should render once when there are no
|
||||
onClick={[Function]}
|
||||
>
|
||||
<ProductMenuButton
|
||||
active={true}
|
||||
aria-controls="product-switcher-menu"
|
||||
aria-expanded={true}
|
||||
aria-label="Product switch menu"
|
||||
/>
|
||||
<ProductBranding />
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(var(--sidebar-text-rgb), 0.16)",
|
||||
"color": "rgba(var(--sidebar-text-rgb), 0.56)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ProductsIcon
|
||||
color="rgba(var(--sidebar-text-rgb), 0.56)"
|
||||
size={20}
|
||||
/>
|
||||
<ProductBranding />
|
||||
</ProductMenuButton>
|
||||
</ProductMenuContainer>
|
||||
<Menu
|
||||
ariaLabel="switcherOpen"
|
||||
@@ -331,12 +381,22 @@ exports[`components/global/product_switcher should render the correct amount of
|
||||
onClick={[Function]}
|
||||
>
|
||||
<ProductMenuButton
|
||||
active={true}
|
||||
aria-controls="product-switcher-menu"
|
||||
aria-expanded={true}
|
||||
aria-label="Product switch menu"
|
||||
/>
|
||||
<ProductBranding />
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(var(--sidebar-text-rgb), 0.16)",
|
||||
"color": "rgba(var(--sidebar-text-rgb), 0.56)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ProductsIcon
|
||||
color="rgba(var(--sidebar-text-rgb), 0.56)"
|
||||
size={20}
|
||||
/>
|
||||
<ProductBranding />
|
||||
</ProductMenuButton>
|
||||
</ProductMenuContainer>
|
||||
<Menu
|
||||
ariaLabel="switcherOpen"
|
||||
|
||||
@@ -2,51 +2,63 @@
|
||||
|
||||
exports[`components/ProductBranding should show correct icon glyph when we are on Boards 1`] = `
|
||||
<ProductBrandingContainer
|
||||
tabIndex={0}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ProductBoardsIcon
|
||||
size={24}
|
||||
/>
|
||||
<Heading
|
||||
className="sr-only"
|
||||
element="h1"
|
||||
margin="none"
|
||||
size={200}
|
||||
>
|
||||
Boards
|
||||
</Heading>
|
||||
<ProductBrandingHeading>
|
||||
Boards
|
||||
</ProductBrandingHeading>
|
||||
</ProductBrandingContainer>
|
||||
`;
|
||||
|
||||
exports[`components/ProductBranding should show correct icon glyph when we are on Channels 1`] = `
|
||||
<ProductBrandingContainer
|
||||
tabIndex={0}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ProductChannelsIcon
|
||||
size={24}
|
||||
/>
|
||||
<Heading
|
||||
className="sr-only"
|
||||
element="h1"
|
||||
margin="none"
|
||||
size={200}
|
||||
>
|
||||
Channels
|
||||
</Heading>
|
||||
<ProductBrandingHeading>
|
||||
Channels
|
||||
</ProductBrandingHeading>
|
||||
</ProductBrandingContainer>
|
||||
`;
|
||||
|
||||
exports[`components/ProductBranding should show correct icon glyph when we are on Playbooks 1`] = `
|
||||
<ProductBrandingContainer
|
||||
tabIndex={0}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ProductPlaybooksIcon
|
||||
size={24}
|
||||
/>
|
||||
<Heading
|
||||
className="sr-only"
|
||||
element="h1"
|
||||
margin="none"
|
||||
size={200}
|
||||
>
|
||||
Playbooks
|
||||
</Heading>
|
||||
<ProductBrandingHeading>
|
||||
Playbooks
|
||||
</ProductBrandingHeading>
|
||||
</ProductBrandingContainer>
|
||||
`;
|
||||
|
||||
@@ -9,13 +9,22 @@ import glyphMap, {ProductChannelsIcon} from '@mattermost/compass-icons/component
|
||||
|
||||
import {useCurrentProduct} from 'utils/products';
|
||||
|
||||
const ProductBrandingContainer = styled.div`
|
||||
const ProductBrandingContainer = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
> * + * {
|
||||
margin-left: 8px;
|
||||
}
|
||||
// Every style here except for 'margin-left' and 'font-family'is from the deprecated 'Heading' element.
|
||||
// https://github.com/mattermost/compass-components/blob/362e96a4eb3489efc8c1852652859ef14a51eb64/src/components/heading/Heading.mixins.ts#L9-L74
|
||||
const ProductBrandingHeading = styled.span`
|
||||
font-family: 'Metropolis';
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
color: inherit;
|
||||
|
||||
margin-left: 8px;
|
||||
`;
|
||||
|
||||
const ProductBranding = (): JSX.Element => {
|
||||
@@ -24,15 +33,22 @@ const ProductBranding = (): JSX.Element => {
|
||||
const Icon = currentProduct?.switcherIcon ? glyphMap[currentProduct.switcherIcon] : ProductChannelsIcon;
|
||||
|
||||
return (
|
||||
<ProductBrandingContainer tabIndex={0}>
|
||||
<ProductBrandingContainer tabIndex={-1}>
|
||||
<Icon size={24}/>
|
||||
|
||||
{/* Heading for screen readers since an h1 shouldn't be inside a button */}
|
||||
<Heading
|
||||
element='h1'
|
||||
size={200}
|
||||
margin='none'
|
||||
className='sr-only'
|
||||
>
|
||||
{currentProduct ? currentProduct.switcherText : 'Channels'}
|
||||
</Heading>
|
||||
|
||||
<ProductBrandingHeading>
|
||||
{currentProduct ? currentProduct.switcherText : 'Channels'}
|
||||
</ProductBrandingHeading>
|
||||
</ProductBrandingContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import styled from 'styled-components';
|
||||
|
||||
import Logo from 'components/common/svg_images_components/logo_dark_blue_svg';
|
||||
|
||||
const ProductBrandingTeamEditionContainer = styled.div`
|
||||
const ProductBrandingTeamEditionContainer = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -21,12 +21,10 @@ const StyledLogo = styled(Logo)`
|
||||
}
|
||||
`;
|
||||
|
||||
const Badge = styled.div`
|
||||
const Badge = styled.span`
|
||||
display: flex;
|
||||
align-self: center;
|
||||
padding: 2px 6px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
border-radius: var(--radius-s);
|
||||
margin-left: 12px;
|
||||
background: rgba(var(--sidebar-text-rgb), 0.08);
|
||||
@@ -40,7 +38,7 @@ const Badge = styled.div`
|
||||
|
||||
const ProductBrandingTeamEdition = (): JSX.Element => {
|
||||
return (
|
||||
<ProductBrandingTeamEditionContainer tabIndex={0}>
|
||||
<ProductBrandingTeamEditionContainer tabIndex={-1}>
|
||||
<StyledLogo
|
||||
width={116}
|
||||
height={20}
|
||||
|
||||
@@ -159,7 +159,7 @@ describe('components/global/product_switcher', () => {
|
||||
useStateSpy.mockImplementation(() => [false, setState]);
|
||||
|
||||
wrapper.find(ProductMenuContainer).simulate('click');
|
||||
expect(wrapper.find(ProductMenuButton).props().active).toEqual(true);
|
||||
expect(wrapper.find(ProductMenuButton).props()['aria-expanded']).toEqual(true);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ import {useIntl} from 'react-intl';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import IconButton from '@mattermost/compass-components/components/icon-button'; // eslint-disable-line no-restricted-imports
|
||||
import {
|
||||
ProductsIcon,
|
||||
} from '@mattermost/compass-icons/components';
|
||||
|
||||
import {getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
@@ -41,21 +43,29 @@ export const ProductMenuContainer = styled.nav`
|
||||
}
|
||||
`;
|
||||
|
||||
export const ProductMenuButton = styled(IconButton).attrs(() => ({
|
||||
export const ProductMenuButton = styled.button.attrs(() => ({
|
||||
id: 'product_switch_menu',
|
||||
icon: 'products',
|
||||
size: 'sm',
|
||||
|
||||
// we currently need this, since not passing a onClick handler is disabling the IconButton
|
||||
// this is a known issue and is being tracked by UI platform team
|
||||
// TODO@UI: remove the onClick, when it is not a mandatory prop anymore
|
||||
onClick: () => {},
|
||||
inverted: true,
|
||||
compact: true,
|
||||
type: 'button',
|
||||
}))`
|
||||
> i::before {
|
||||
font-size: 20px;
|
||||
letter-spacing: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 3px 6px 3px 5px;
|
||||
|
||||
&:hover, &:focus {
|
||||
color: rgba(var(--sidebar-text-rgb), 0.56);
|
||||
background-color: rgba(var(--sidebar-text-rgb), 0.08);
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: rgba(var(--sidebar-text-rgb), 0.56);
|
||||
background-color: rgba(var(--sidebar-text-rgb), 0.16);
|
||||
}
|
||||
|
||||
> * + * {
|
||||
margin-left: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -110,13 +120,21 @@ const ProductMenu = (): JSX.Element => {
|
||||
>
|
||||
<ProductMenuContainer onClick={handleClick}>
|
||||
<ProductMenuButton
|
||||
active={switcherOpen}
|
||||
aria-expanded={switcherOpen}
|
||||
aria-label={formatMessage({id: 'global_header.productSwitchMenu', defaultMessage: 'Product switch menu'})}
|
||||
aria-controls='product-switcher-menu'
|
||||
/>
|
||||
{license.IsLicensed === 'false' && <ProductBrandingTeamEdition/>}
|
||||
{license.IsLicensed === 'true' && <ProductBranding/>}
|
||||
style={switcherOpen ? {
|
||||
backgroundColor: 'rgba(var(--sidebar-text-rgb), 0.16)',
|
||||
color: 'rgba(var(--sidebar-text-rgb), 0.56)',
|
||||
} : {}}
|
||||
>
|
||||
<ProductsIcon
|
||||
size={20}
|
||||
color='rgba(var(--sidebar-text-rgb), 0.56)'
|
||||
/>
|
||||
{license.IsLicensed === 'false' && <ProductBrandingTeamEdition/>}
|
||||
{license.IsLicensed === 'true' && <ProductBranding/>}
|
||||
</ProductMenuButton>
|
||||
</ProductMenuContainer>
|
||||
<Menu
|
||||
listId={'product-switcher-menu-dropdown'}
|
||||
|
||||
Ссылка в новой задаче
Block a user