fix(accessibility): tab support at login, reset and signup pages, buttons at ATE and app bar (#24214)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
45a14e23a9
Коммит
ecf7cdbdea
@@ -71,6 +71,7 @@ export const IconContainer = styled.button`
|
||||
`;
|
||||
|
||||
interface FormattingIconProps {
|
||||
id?: string;
|
||||
mode: MarkdownMode;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
@@ -129,7 +130,7 @@ const FormattingIcon = (props: FormattingIconProps): JSX.Element => {
|
||||
const bodyAction = (
|
||||
<IconContainer
|
||||
type='button'
|
||||
id={`FormattingControl_${mode}`}
|
||||
id={props.id || `FormattingControl_${mode}`}
|
||||
onClick={onClick}
|
||||
aria-label={buttonAriaLabel}
|
||||
{...otherProps}
|
||||
|
||||
@@ -50,6 +50,7 @@ const AlertBanner = ({
|
||||
children,
|
||||
}: AlertBannerProps) => {
|
||||
const {formatMessage} = useIntl();
|
||||
const closeText = formatMessage({id: 'alert_banner.tooltipCloseBtn', defaultMessage: 'Close'});
|
||||
const [tooltipId] = useState(`alert_banner_close_btn_tooltip_${Math.random()}`);
|
||||
|
||||
const bannerIcon = useCallback(() => {
|
||||
@@ -110,12 +111,11 @@ const AlertBanner = ({
|
||||
delayShow={Constants.OVERLAY_TIME_DELAY}
|
||||
placement='left'
|
||||
overlay={closeBtnTooltip || (
|
||||
<Tooltip id={tooltipId}>
|
||||
{formatMessage({id: 'alert_banner.tooltipCloseBtn', defaultMessage: 'Close'})}
|
||||
</Tooltip>
|
||||
<Tooltip id={tooltipId}>{closeText}</Tooltip>
|
||||
)}
|
||||
>
|
||||
<button
|
||||
aria-label={closeText}
|
||||
className='AlertBanner__closeButton'
|
||||
onClick={onDismiss}
|
||||
>
|
||||
|
||||
@@ -76,6 +76,8 @@ exports[`components/app_bar/app_bar should match snapshot on mount 1`] = `
|
||||
>
|
||||
<div
|
||||
className="app-bar__old-icon app-bar__icon-inner app-bar__icon-inner--centered"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
fallback_component
|
||||
</div>
|
||||
@@ -241,6 +243,8 @@ exports[`components/app_bar/app_bar should match snapshot on mount when App Bar
|
||||
>
|
||||
<div
|
||||
className="app-bar__old-icon app-bar__icon-inner app-bar__icon-inner--centered"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
fallback_component
|
||||
</div>
|
||||
|
||||
@@ -64,7 +64,11 @@ const AppBarPluginComponent = (props: PluginComponentProps) => {
|
||||
|
||||
const iconUrl = component.iconUrl;
|
||||
let content: React.ReactNode = (
|
||||
<div className='app-bar__icon-inner'>
|
||||
<div
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
className='app-bar__icon-inner'
|
||||
>
|
||||
<img
|
||||
src={iconUrl}
|
||||
onLoad={onImageLoadComplete}
|
||||
@@ -77,7 +81,11 @@ const AppBarPluginComponent = (props: PluginComponentProps) => {
|
||||
|
||||
if (!iconUrl) {
|
||||
content = (
|
||||
<div className={classNames('app-bar__old-icon app-bar__icon-inner app-bar__icon-inner--centered', {'app-bar__old-icon--active': isButtonActive})}>
|
||||
<div
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
className={classNames('app-bar__old-icon app-bar__icon-inner app-bar__icon-inner--centered', {'app-bar__old-icon--active': isButtonActive})}
|
||||
>
|
||||
{component.icon}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -25,11 +25,9 @@ const BackButton = ({url, className, onClick}: Props): JSX.Element => {
|
||||
const {formatMessage} = useIntl();
|
||||
|
||||
return (
|
||||
<div
|
||||
id='back_button'
|
||||
className={classNames('signup-header', className)}
|
||||
>
|
||||
<div className={classNames('signup-header', className)}>
|
||||
<Link
|
||||
data-testid='back_button'
|
||||
onClick={onClick}
|
||||
to={url}
|
||||
>
|
||||
|
||||
@@ -43,10 +43,7 @@ exports[`components/HeaderFooterTemplate should match snapshot with about link 1
|
||||
key="about_link"
|
||||
location="header_footer_template"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="About"
|
||||
id="web.footer.about"
|
||||
/>
|
||||
About
|
||||
</ExternalLink>
|
||||
</span>
|
||||
</div>
|
||||
@@ -98,10 +95,7 @@ exports[`components/HeaderFooterTemplate should match snapshot with all links 1`
|
||||
key="about_link"
|
||||
location="header_footer_template"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="About"
|
||||
id="web.footer.about"
|
||||
/>
|
||||
About
|
||||
</ExternalLink>
|
||||
<ExternalLink
|
||||
className="footer-link"
|
||||
@@ -110,10 +104,7 @@ exports[`components/HeaderFooterTemplate should match snapshot with all links 1`
|
||||
key="privacy_link"
|
||||
location="header_footer_template"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Privacy Policy"
|
||||
id="web.footer.privacy"
|
||||
/>
|
||||
Privacy Policy
|
||||
</ExternalLink>
|
||||
<ExternalLink
|
||||
className="footer-link"
|
||||
@@ -122,10 +113,7 @@ exports[`components/HeaderFooterTemplate should match snapshot with all links 1`
|
||||
key="terms_link"
|
||||
location="header_footer_template"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Terms"
|
||||
id="web.footer.terms"
|
||||
/>
|
||||
Terms
|
||||
</ExternalLink>
|
||||
<ExternalLink
|
||||
className="footer-link"
|
||||
@@ -134,10 +122,7 @@ exports[`components/HeaderFooterTemplate should match snapshot with all links 1`
|
||||
key="help_link"
|
||||
location="header_footer_template"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Help"
|
||||
id="web.footer.help"
|
||||
/>
|
||||
Help
|
||||
</ExternalLink>
|
||||
</span>
|
||||
</div>
|
||||
@@ -235,10 +220,7 @@ exports[`components/HeaderFooterTemplate should match snapshot with help link 1`
|
||||
key="help_link"
|
||||
location="header_footer_template"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Help"
|
||||
id="web.footer.help"
|
||||
/>
|
||||
Help
|
||||
</ExternalLink>
|
||||
</span>
|
||||
</div>
|
||||
@@ -290,10 +272,7 @@ exports[`components/HeaderFooterTemplate should match snapshot with privacy poli
|
||||
key="privacy_link"
|
||||
location="header_footer_template"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Privacy Policy"
|
||||
id="web.footer.privacy"
|
||||
/>
|
||||
Privacy Policy
|
||||
</ExternalLink>
|
||||
</span>
|
||||
</div>
|
||||
@@ -345,10 +324,7 @@ exports[`components/HeaderFooterTemplate should match snapshot with term of serv
|
||||
key="terms_link"
|
||||
location="header_footer_template"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Terms"
|
||||
id="web.footer.terms"
|
||||
/>
|
||||
Terms
|
||||
</ExternalLink>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {ClientConfig} from '@mattermost/types/config';
|
||||
import ExternalLink from 'components/external_link';
|
||||
import {localizeMessage} from 'utils/utils';
|
||||
|
||||
type Props = {
|
||||
config: Partial<ClientConfig> | undefined;
|
||||
@@ -57,10 +57,7 @@ export default class NotLoggedIn extends React.PureComponent<Props> {
|
||||
location='header_footer_template'
|
||||
href={this.props.config.AboutLink}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='web.footer.about'
|
||||
defaultMessage='About'
|
||||
/>
|
||||
{localizeMessage('web.footer.about', 'About')}
|
||||
</ExternalLink>,
|
||||
);
|
||||
}
|
||||
@@ -74,10 +71,7 @@ export default class NotLoggedIn extends React.PureComponent<Props> {
|
||||
location='header_footer_template'
|
||||
href={this.props.config.PrivacyPolicyLink}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='web.footer.privacy'
|
||||
defaultMessage='Privacy Policy'
|
||||
/>
|
||||
{localizeMessage('web.footer.privacy', 'Privacy Policy')}
|
||||
</ExternalLink>,
|
||||
);
|
||||
}
|
||||
@@ -91,10 +85,7 @@ export default class NotLoggedIn extends React.PureComponent<Props> {
|
||||
location='header_footer_template'
|
||||
href={this.props.config.TermsOfServiceLink}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='web.footer.terms'
|
||||
defaultMessage='Terms'
|
||||
/>
|
||||
{localizeMessage('web.footer.terms', 'Terms')}
|
||||
</ExternalLink>,
|
||||
);
|
||||
}
|
||||
@@ -108,10 +99,7 @@ export default class NotLoggedIn extends React.PureComponent<Props> {
|
||||
location='header_footer_template'
|
||||
href={this.props.config.HelpLink}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='web.footer.help'
|
||||
defaultMessage='Help'
|
||||
/>
|
||||
{localizeMessage('web.footer.help', 'Help')}
|
||||
</ExternalLink>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -363,8 +363,6 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
|
||||
const onWindowFocus = useCallback(() => {
|
||||
if (extraParam === Constants.SIGNIN_VERIFIED && emailParam) {
|
||||
passwordInput.current?.focus();
|
||||
} else {
|
||||
loginIdInput.current?.focus();
|
||||
}
|
||||
}, [emailParam, extraParam]);
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ function PostPriorityPickerOverlay({
|
||||
{...getTooltipReferenceProps()}
|
||||
>
|
||||
<IconContainer
|
||||
id='messagePriority'
|
||||
ref={pickerRef}
|
||||
className={classNames({control: true, active: pickerOpen})}
|
||||
disabled={disabled}
|
||||
|
||||
@@ -594,6 +594,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
||||
return (
|
||||
<CheckInput
|
||||
id='signup-body-card-form-check-newsletter'
|
||||
ariaLabel={formatMessage({id: 'newsletter_optin.checkmark.box', defaultMessage: 'newsletter checkbox'})}
|
||||
name='newsletter'
|
||||
onChange={() => setSubscribeToSecurityNewsletter(!subscribeToSecurityNewsletter)}
|
||||
text={
|
||||
|
||||
@@ -6,6 +6,7 @@ import './check.scss';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
ariaLabel: string;
|
||||
name: string;
|
||||
text: ReactNode;
|
||||
onChange: () => void;
|
||||
@@ -13,14 +14,17 @@ type Props = {
|
||||
}
|
||||
|
||||
function CheckInput(props: Props) {
|
||||
const {id, ariaLabel, text, ...rest} = props;
|
||||
|
||||
return (
|
||||
<div className='check-input'>
|
||||
<input
|
||||
{...props}
|
||||
data-testid={props.id}
|
||||
{...rest}
|
||||
aria-label={ariaLabel}
|
||||
data-testid={id}
|
||||
type='checkbox'
|
||||
/>
|
||||
<span className='text'>{props.text}</span>
|
||||
<span className='text'>{text}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ const PasswordInput = React.forwardRef((
|
||||
<button
|
||||
id='password_toggle'
|
||||
type='button'
|
||||
aria-label={placeHolder}
|
||||
aria-label={formatMessage({id: 'widget.passwordInput.passwordToggle', defaultMessage: 'Show or hide password'})}
|
||||
className='password-input-toggle'
|
||||
onClick={toggleShowPassword}
|
||||
disabled={disabled}
|
||||
|
||||
@@ -4100,6 +4100,7 @@
|
||||
"navbar.viewPinnedPosts": "View Pinned Posts",
|
||||
"newChannelWithBoard.tutorialTip.description": "The board you just created can be quickly accessed by clicking on the Boards icon in the App bar. You can view the boards that are linked to this channel in the right-hand sidebar and open one in full view.",
|
||||
"newChannelWithBoard.tutorialTip.title": "Access linked boards from the App Bar",
|
||||
"newsletter_optin.checkmark.box": "newsletter checkbox",
|
||||
"newsletter_optin.checkmark.text": "<span>I would like to receive Mattermost security updates via newsletter.</span> By subscribing, I consent to receive emails from Mattermost with product updates, promotions, and company news. I have read the <a>Privacy Policy</a> and understand that I can <aa>unsubscribe</aa> at any time",
|
||||
"newsletter_optin.desc": "Sign up at <a>{link}</a>.",
|
||||
"newsletter_optin.title": "Interested in receiving Mattermost security, product, promotions, and company updates updates via newsletter?",
|
||||
@@ -5589,6 +5590,7 @@
|
||||
"widget.input.required": "This field is required",
|
||||
"widget.passwordInput.createPassword": "Choose a Password",
|
||||
"widget.passwordInput.password": "Password",
|
||||
"widget.passwordInput.passwordToggle": "Show or hide password",
|
||||
"widgets.channels_input.empty": "No channels found",
|
||||
"widgets.channels_input.loading": "Loading",
|
||||
"widgets.users_emails_input.loading": "Loading",
|
||||
|
||||
Ссылка в новой задаче
Block a user