[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]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
<ProductMenuButton
|
<ProductMenuButton
|
||||||
active={true}
|
|
||||||
aria-controls="product-switcher-menu"
|
aria-controls="product-switcher-menu"
|
||||||
aria-expanded={true}
|
aria-expanded={true}
|
||||||
aria-label="Product switch menu"
|
aria-label="Product switch menu"
|
||||||
/>
|
style={
|
||||||
<ProductBranding />
|
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>
|
</ProductMenuContainer>
|
||||||
<Menu
|
<Menu
|
||||||
ariaLabel="switcherOpen"
|
ariaLabel="switcherOpen"
|
||||||
@@ -75,12 +85,22 @@ exports[`components/global/product_switcher should match snapshot 1`] = `
|
|||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
<ProductMenuButton
|
<ProductMenuButton
|
||||||
active={true}
|
|
||||||
aria-controls="product-switcher-menu"
|
aria-controls="product-switcher-menu"
|
||||||
aria-expanded={true}
|
aria-expanded={true}
|
||||||
aria-label="Product switch menu"
|
aria-label="Product switch menu"
|
||||||
/>
|
style={
|
||||||
<ProductBranding />
|
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>
|
</ProductMenuContainer>
|
||||||
<Menu
|
<Menu
|
||||||
ariaLabel="switcherOpen"
|
ariaLabel="switcherOpen"
|
||||||
@@ -139,12 +159,22 @@ exports[`components/global/product_switcher should match snapshot with product s
|
|||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
<ProductMenuButton
|
<ProductMenuButton
|
||||||
active={true}
|
|
||||||
aria-controls="product-switcher-menu"
|
aria-controls="product-switcher-menu"
|
||||||
aria-expanded={true}
|
aria-expanded={true}
|
||||||
aria-label="Product switch menu"
|
aria-label="Product switch menu"
|
||||||
/>
|
style={
|
||||||
<ProductBranding />
|
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>
|
</ProductMenuContainer>
|
||||||
<Menu
|
<Menu
|
||||||
ariaLabel="switcherOpen"
|
ariaLabel="switcherOpen"
|
||||||
@@ -203,12 +233,22 @@ exports[`components/global/product_switcher should match snapshot without licens
|
|||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
<ProductMenuButton
|
<ProductMenuButton
|
||||||
active={true}
|
|
||||||
aria-controls="product-switcher-menu"
|
aria-controls="product-switcher-menu"
|
||||||
aria-expanded={true}
|
aria-expanded={true}
|
||||||
aria-label="Product switch menu"
|
aria-label="Product switch menu"
|
||||||
/>
|
style={
|
||||||
<ProductBrandingTeamEdition />
|
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>
|
</ProductMenuContainer>
|
||||||
<Menu
|
<Menu
|
||||||
ariaLabel="switcherOpen"
|
ariaLabel="switcherOpen"
|
||||||
@@ -267,12 +307,22 @@ exports[`components/global/product_switcher should render once when there are no
|
|||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
<ProductMenuButton
|
<ProductMenuButton
|
||||||
active={true}
|
|
||||||
aria-controls="product-switcher-menu"
|
aria-controls="product-switcher-menu"
|
||||||
aria-expanded={true}
|
aria-expanded={true}
|
||||||
aria-label="Product switch menu"
|
aria-label="Product switch menu"
|
||||||
/>
|
style={
|
||||||
<ProductBranding />
|
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>
|
</ProductMenuContainer>
|
||||||
<Menu
|
<Menu
|
||||||
ariaLabel="switcherOpen"
|
ariaLabel="switcherOpen"
|
||||||
@@ -331,12 +381,22 @@ exports[`components/global/product_switcher should render the correct amount of
|
|||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
<ProductMenuButton
|
<ProductMenuButton
|
||||||
active={true}
|
|
||||||
aria-controls="product-switcher-menu"
|
aria-controls="product-switcher-menu"
|
||||||
aria-expanded={true}
|
aria-expanded={true}
|
||||||
aria-label="Product switch menu"
|
aria-label="Product switch menu"
|
||||||
/>
|
style={
|
||||||
<ProductBranding />
|
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>
|
</ProductMenuContainer>
|
||||||
<Menu
|
<Menu
|
||||||
ariaLabel="switcherOpen"
|
ariaLabel="switcherOpen"
|
||||||
|
|||||||
@@ -2,51 +2,63 @@
|
|||||||
|
|
||||||
exports[`components/ProductBranding should show correct icon glyph when we are on Boards 1`] = `
|
exports[`components/ProductBranding should show correct icon glyph when we are on Boards 1`] = `
|
||||||
<ProductBrandingContainer
|
<ProductBrandingContainer
|
||||||
tabIndex={0}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<ProductBoardsIcon
|
<ProductBoardsIcon
|
||||||
size={24}
|
size={24}
|
||||||
/>
|
/>
|
||||||
<Heading
|
<Heading
|
||||||
|
className="sr-only"
|
||||||
element="h1"
|
element="h1"
|
||||||
margin="none"
|
margin="none"
|
||||||
size={200}
|
size={200}
|
||||||
>
|
>
|
||||||
Boards
|
Boards
|
||||||
</Heading>
|
</Heading>
|
||||||
|
<ProductBrandingHeading>
|
||||||
|
Boards
|
||||||
|
</ProductBrandingHeading>
|
||||||
</ProductBrandingContainer>
|
</ProductBrandingContainer>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`components/ProductBranding should show correct icon glyph when we are on Channels 1`] = `
|
exports[`components/ProductBranding should show correct icon glyph when we are on Channels 1`] = `
|
||||||
<ProductBrandingContainer
|
<ProductBrandingContainer
|
||||||
tabIndex={0}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<ProductChannelsIcon
|
<ProductChannelsIcon
|
||||||
size={24}
|
size={24}
|
||||||
/>
|
/>
|
||||||
<Heading
|
<Heading
|
||||||
|
className="sr-only"
|
||||||
element="h1"
|
element="h1"
|
||||||
margin="none"
|
margin="none"
|
||||||
size={200}
|
size={200}
|
||||||
>
|
>
|
||||||
Channels
|
Channels
|
||||||
</Heading>
|
</Heading>
|
||||||
|
<ProductBrandingHeading>
|
||||||
|
Channels
|
||||||
|
</ProductBrandingHeading>
|
||||||
</ProductBrandingContainer>
|
</ProductBrandingContainer>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`components/ProductBranding should show correct icon glyph when we are on Playbooks 1`] = `
|
exports[`components/ProductBranding should show correct icon glyph when we are on Playbooks 1`] = `
|
||||||
<ProductBrandingContainer
|
<ProductBrandingContainer
|
||||||
tabIndex={0}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<ProductPlaybooksIcon
|
<ProductPlaybooksIcon
|
||||||
size={24}
|
size={24}
|
||||||
/>
|
/>
|
||||||
<Heading
|
<Heading
|
||||||
|
className="sr-only"
|
||||||
element="h1"
|
element="h1"
|
||||||
margin="none"
|
margin="none"
|
||||||
size={200}
|
size={200}
|
||||||
>
|
>
|
||||||
Playbooks
|
Playbooks
|
||||||
</Heading>
|
</Heading>
|
||||||
|
<ProductBrandingHeading>
|
||||||
|
Playbooks
|
||||||
|
</ProductBrandingHeading>
|
||||||
</ProductBrandingContainer>
|
</ProductBrandingContainer>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -9,13 +9,22 @@ import glyphMap, {ProductChannelsIcon} from '@mattermost/compass-icons/component
|
|||||||
|
|
||||||
import {useCurrentProduct} from 'utils/products';
|
import {useCurrentProduct} from 'utils/products';
|
||||||
|
|
||||||
const ProductBrandingContainer = styled.div`
|
const ProductBrandingContainer = styled.span`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
> * + * {
|
// Every style here except for 'margin-left' and 'font-family'is from the deprecated 'Heading' element.
|
||||||
margin-left: 8px;
|
// 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 => {
|
const ProductBranding = (): JSX.Element => {
|
||||||
@@ -24,15 +33,22 @@ const ProductBranding = (): JSX.Element => {
|
|||||||
const Icon = currentProduct?.switcherIcon ? glyphMap[currentProduct.switcherIcon] : ProductChannelsIcon;
|
const Icon = currentProduct?.switcherIcon ? glyphMap[currentProduct.switcherIcon] : ProductChannelsIcon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProductBrandingContainer tabIndex={0}>
|
<ProductBrandingContainer tabIndex={-1}>
|
||||||
<Icon size={24}/>
|
<Icon size={24}/>
|
||||||
|
|
||||||
|
{/* Heading for screen readers since an h1 shouldn't be inside a button */}
|
||||||
<Heading
|
<Heading
|
||||||
element='h1'
|
element='h1'
|
||||||
size={200}
|
size={200}
|
||||||
margin='none'
|
margin='none'
|
||||||
|
className='sr-only'
|
||||||
>
|
>
|
||||||
{currentProduct ? currentProduct.switcherText : 'Channels'}
|
{currentProduct ? currentProduct.switcherText : 'Channels'}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
|
<ProductBrandingHeading>
|
||||||
|
{currentProduct ? currentProduct.switcherText : 'Channels'}
|
||||||
|
</ProductBrandingHeading>
|
||||||
</ProductBrandingContainer>
|
</ProductBrandingContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import Logo from 'components/common/svg_images_components/logo_dark_blue_svg';
|
import Logo from 'components/common/svg_images_components/logo_dark_blue_svg';
|
||||||
|
|
||||||
const ProductBrandingTeamEditionContainer = styled.div`
|
const ProductBrandingTeamEditionContainer = styled.span`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
@@ -21,12 +21,10 @@ const StyledLogo = styled(Logo)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Badge = styled.div`
|
const Badge = styled.span`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
position: relative;
|
|
||||||
top: 1px;
|
|
||||||
border-radius: var(--radius-s);
|
border-radius: var(--radius-s);
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
background: rgba(var(--sidebar-text-rgb), 0.08);
|
background: rgba(var(--sidebar-text-rgb), 0.08);
|
||||||
@@ -40,7 +38,7 @@ const Badge = styled.div`
|
|||||||
|
|
||||||
const ProductBrandingTeamEdition = (): JSX.Element => {
|
const ProductBrandingTeamEdition = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<ProductBrandingTeamEditionContainer tabIndex={0}>
|
<ProductBrandingTeamEditionContainer tabIndex={-1}>
|
||||||
<StyledLogo
|
<StyledLogo
|
||||||
width={116}
|
width={116}
|
||||||
height={20}
|
height={20}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ describe('components/global/product_switcher', () => {
|
|||||||
useStateSpy.mockImplementation(() => [false, setState]);
|
useStateSpy.mockImplementation(() => [false, setState]);
|
||||||
|
|
||||||
wrapper.find(ProductMenuContainer).simulate('click');
|
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();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import {useIntl} from 'react-intl';
|
|||||||
import {useDispatch, useSelector} from 'react-redux';
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
import styled from 'styled-components';
|
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';
|
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',
|
id: 'product_switch_menu',
|
||||||
icon: 'products',
|
type: 'button',
|
||||||
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,
|
|
||||||
}))`
|
}))`
|
||||||
> i::before {
|
display: flex;
|
||||||
font-size: 20px;
|
align-items: center;
|
||||||
letter-spacing: 20px;
|
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}>
|
<ProductMenuContainer onClick={handleClick}>
|
||||||
<ProductMenuButton
|
<ProductMenuButton
|
||||||
active={switcherOpen}
|
|
||||||
aria-expanded={switcherOpen}
|
aria-expanded={switcherOpen}
|
||||||
aria-label={formatMessage({id: 'global_header.productSwitchMenu', defaultMessage: 'Product switch menu'})}
|
aria-label={formatMessage({id: 'global_header.productSwitchMenu', defaultMessage: 'Product switch menu'})}
|
||||||
aria-controls='product-switcher-menu'
|
aria-controls='product-switcher-menu'
|
||||||
/>
|
style={switcherOpen ? {
|
||||||
{license.IsLicensed === 'false' && <ProductBrandingTeamEdition/>}
|
backgroundColor: 'rgba(var(--sidebar-text-rgb), 0.16)',
|
||||||
{license.IsLicensed === 'true' && <ProductBranding/>}
|
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>
|
</ProductMenuContainer>
|
||||||
<Menu
|
<Menu
|
||||||
listId={'product-switcher-menu-dropdown'}
|
listId={'product-switcher-menu-dropdown'}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user