[MM-62973][MM-63028][MM-63034][MM-63027] Various accessibility fixes around Browse Channels modal (#30922)
* [MM-62973] Fix order of Close button and other buttons in the header * [MM-63028] Add aria-live status area when searching for channels * [MM-63027] Add announcement that channel is joined * [MM-63034] Add role=checkbox and reformat HTML to have correct custom checkbox code * Update snapshots from generic_modal change * PR feedback * PR feedback * PR feedback
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c82a24f396
Коммит
883711c72d
@@ -121,11 +121,15 @@ exports[`components/SearchableChannelList should match init snapshot 1`] = `
|
||||
/>
|
||||
</Menu>
|
||||
<div
|
||||
aria-checked={false}
|
||||
aria-label="Hide joined channels"
|
||||
id="hideJoinedPreferenceCheckbox"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
role="checkbox"
|
||||
tabIndex={0}
|
||||
>
|
||||
<button
|
||||
aria-label="Hide joined channels checkbox, not checked"
|
||||
<div
|
||||
className="get-app__checkbox"
|
||||
/>
|
||||
<MemoizedFormattedMessage
|
||||
|
||||
@@ -40,22 +40,6 @@ Object {
|
||||
<div
|
||||
class="modal-header"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="GenericModal__header__text_container"
|
||||
>
|
||||
@@ -87,6 +71,22 @@ Object {
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="modal-body"
|
||||
@@ -522,22 +522,6 @@ Object {
|
||||
<div
|
||||
class="modal-header"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="GenericModal__header__text_container"
|
||||
>
|
||||
@@ -569,6 +553,22 @@ Object {
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="modal-body"
|
||||
@@ -1164,22 +1164,6 @@ Object {
|
||||
<div
|
||||
class="modal-header"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="GenericModal__header__text_container"
|
||||
>
|
||||
@@ -1211,6 +1195,22 @@ Object {
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="modal-body"
|
||||
@@ -1525,22 +1525,6 @@ Object {
|
||||
<div
|
||||
class="modal-header"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="GenericModal__header__text_container"
|
||||
>
|
||||
@@ -1572,6 +1556,22 @@ Object {
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="modal-body"
|
||||
@@ -2105,22 +2105,6 @@ Object {
|
||||
<div
|
||||
class="modal-header"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="GenericModal__header__text_container"
|
||||
>
|
||||
@@ -2152,6 +2136,22 @@ Object {
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="modal-body"
|
||||
@@ -2685,22 +2685,6 @@ Object {
|
||||
<div
|
||||
class="modal-header"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="GenericModal__header__text_container"
|
||||
>
|
||||
@@ -2732,6 +2716,22 @@ Object {
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
class="sr-only"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="modal-body"
|
||||
|
||||
@@ -40,7 +40,7 @@ exports[`components/marketplace/ doesn't show web marketplace banner in FeatureF
|
||||
>
|
||||
<ModalHeader
|
||||
bsClass="modal-header"
|
||||
closeButton={true}
|
||||
closeButton={false}
|
||||
closeLabel="Close"
|
||||
>
|
||||
<div
|
||||
@@ -57,6 +57,26 @@ exports[`components/marketplace/ doesn't show web marketplace banner in FeatureF
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
className="close"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="generic_modal.close"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
bsClass="modal-body"
|
||||
@@ -132,7 +152,7 @@ exports[`components/marketplace/ hides search, shows web marketplace banner in F
|
||||
>
|
||||
<ModalHeader
|
||||
bsClass="modal-header"
|
||||
closeButton={true}
|
||||
closeButton={false}
|
||||
closeLabel="Close"
|
||||
>
|
||||
<div
|
||||
@@ -149,6 +169,26 @@ exports[`components/marketplace/ hides search, shows web marketplace banner in F
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
className="close"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="generic_modal.close"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
bsClass="modal-body"
|
||||
@@ -225,7 +265,7 @@ exports[`components/marketplace/ should render default 1`] = `
|
||||
>
|
||||
<ModalHeader
|
||||
bsClass="modal-header"
|
||||
closeButton={true}
|
||||
closeButton={false}
|
||||
closeLabel="Close"
|
||||
>
|
||||
<div
|
||||
@@ -262,6 +302,26 @@ exports[`components/marketplace/ should render default 1`] = `
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
className="close"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="generic_modal.close"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
bsClass="modal-body"
|
||||
@@ -369,7 +429,7 @@ exports[`components/marketplace/ should render with error banner 1`] = `
|
||||
>
|
||||
<ModalHeader
|
||||
bsClass="modal-header"
|
||||
closeButton={true}
|
||||
closeButton={false}
|
||||
closeLabel="Close"
|
||||
>
|
||||
<div
|
||||
@@ -406,6 +466,26 @@ exports[`components/marketplace/ should render with error banner 1`] = `
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
className="close"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="generic_modal.close"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
bsClass="modal-body"
|
||||
@@ -530,7 +610,7 @@ exports[`components/marketplace/ should render with no plugins available 1`] = `
|
||||
>
|
||||
<ModalHeader
|
||||
bsClass="modal-header"
|
||||
closeButton={true}
|
||||
closeButton={false}
|
||||
closeLabel="Close"
|
||||
>
|
||||
<div
|
||||
@@ -567,6 +647,26 @@ exports[`components/marketplace/ should render with no plugins available 1`] = `
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
className="close"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="generic_modal.close"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
bsClass="modal-body"
|
||||
@@ -682,7 +782,7 @@ exports[`components/marketplace/ should render with plugins available 1`] = `
|
||||
>
|
||||
<ModalHeader
|
||||
bsClass="modal-header"
|
||||
closeButton={true}
|
||||
closeButton={false}
|
||||
closeLabel="Close"
|
||||
>
|
||||
<div
|
||||
@@ -719,6 +819,26 @@ exports[`components/marketplace/ should render with plugins available 1`] = `
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
className="close"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="generic_modal.close"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
bsClass="modal-body"
|
||||
@@ -852,7 +972,7 @@ exports[`components/marketplace/ should render with plugins installed 1`] = `
|
||||
>
|
||||
<ModalHeader
|
||||
bsClass="modal-header"
|
||||
closeButton={true}
|
||||
closeButton={false}
|
||||
closeLabel="Close"
|
||||
>
|
||||
<div
|
||||
@@ -889,6 +1009,26 @@ exports[`components/marketplace/ should render with plugins installed 1`] = `
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close"
|
||||
className="close"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
>
|
||||
×
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Close"
|
||||
id="generic_modal.close"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
bsClass="modal-body"
|
||||
|
||||
@@ -223,6 +223,20 @@ export class SearchableChannelList extends React.PureComponent<Props, State> {
|
||||
</div>
|
||||
<div className='more-modal__actions'>
|
||||
{joinViewChannelButton}
|
||||
{this.state.joiningChannel === channel.id && !this.isMemberOfChannel(channel.id) && (
|
||||
<span
|
||||
className='sr-only'
|
||||
role='alert'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='more_channels.joinedChannel'
|
||||
defaultMessage='Joined channel {channelName}'
|
||||
values={{
|
||||
channelName: channel.display_name,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -530,13 +544,20 @@ export class SearchableChannelList extends React.PureComponent<Props, State> {
|
||||
<div
|
||||
id={'hideJoinedPreferenceCheckbox'}
|
||||
onClick={this.handleChecked}
|
||||
onKeyDown={(e) => {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
this.handleChecked();
|
||||
}
|
||||
}}
|
||||
role='checkbox'
|
||||
aria-checked={this.props.rememberHideJoinedChannelsChecked}
|
||||
aria-label={this.props.intl.formatMessage({id: 'more_channels.hide_joined_channels', defaultMessage: 'Hide joined channels'})}
|
||||
tabIndex={0}
|
||||
>
|
||||
<button
|
||||
className={hideJoinedButtonClass}
|
||||
aria-label={this.props.rememberHideJoinedChannelsChecked ? this.props.intl.formatMessage({id: 'more_channels.hide_joined_checked', defaultMessage: 'Hide joined channels checkbox, checked'}) : this.props.intl.formatMessage({id: 'more_channels.hide_joined_not_checked', defaultMessage: 'Hide joined channels checkbox, not checked'})}
|
||||
>
|
||||
<div className={hideJoinedButtonClass}>
|
||||
{this.props.rememberHideJoinedChannelsChecked ? <CheckboxCheckedIcon/> : null}
|
||||
</button>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='more_channels.hide_joined'
|
||||
defaultMessage='Hide Joined'
|
||||
@@ -558,6 +579,15 @@ export class SearchableChannelList extends React.PureComponent<Props, State> {
|
||||
const dropDownContainer = (
|
||||
<div className='more-modal__dropdown'>
|
||||
<span id='channelCountLabel'>{channelCountLabel}</span>
|
||||
{this.props.isSearch &&
|
||||
<span
|
||||
className='sr-only'
|
||||
role='status'
|
||||
aria-live='polite'
|
||||
>
|
||||
{channelCountLabel}
|
||||
</span>
|
||||
}
|
||||
<div id='modalPreferenceContainer'>
|
||||
{channelDropdown}
|
||||
{hideJoinedPreferenceCheckbox}
|
||||
|
||||
@@ -4499,9 +4499,9 @@
|
||||
"more_channels.count_zero": "0 Results",
|
||||
"more_channels.create": "Create New Channel",
|
||||
"more_channels.hide_joined": "Hide Joined",
|
||||
"more_channels.hide_joined_checked": "Hide joined channels checkbox, checked",
|
||||
"more_channels.hide_joined_not_checked": "Hide joined channels checkbox, not checked",
|
||||
"more_channels.hide_joined_channels": "Hide joined channels",
|
||||
"more_channels.joined": "Joined",
|
||||
"more_channels.joinedChannel": "Joined channel {channelName}",
|
||||
"more_channels.membership_indicator": "Membership Indicator: Joined",
|
||||
"more_channels.next": "Next",
|
||||
"more_channels.noArchived": "No archived channels",
|
||||
|
||||
@@ -39,6 +39,9 @@ exports[`GenericModal should match snapshot for base case 1`] = `
|
||||
<div
|
||||
class="modal-header"
|
||||
>
|
||||
<div
|
||||
class="GenericModal__header__text_container"
|
||||
/>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
@@ -55,9 +58,6 @@ exports[`GenericModal should match snapshot for base case 1`] = `
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="GenericModal__header__text_container"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="modal-body"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import classNames from 'classnames';
|
||||
import React, {useState, useEffect, useCallback, useRef} from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
|
||||
import {useFocusTrap} from '../hooks/useFocusTrap';
|
||||
import './generic_modal.scss';
|
||||
@@ -128,6 +128,8 @@ export const GenericModal: React.FC<Props> = ({
|
||||
dataTestId,
|
||||
delayFocusTrap,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
// Create a ref for the modal container
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -290,7 +292,7 @@ export const GenericModal: React.FC<Props> = ({
|
||||
className='GenericModal__wrapper GenericModal__wrapper-enter-key-press-catcher'
|
||||
>
|
||||
{showHeader && (
|
||||
<Modal.Header closeButton={showCloseButton}>
|
||||
<Modal.Header closeButton={false}>
|
||||
<div className='GenericModal__header__text_container'>
|
||||
{compassDesign && (
|
||||
<>
|
||||
@@ -306,6 +308,19 @@ export const GenericModal: React.FC<Props> = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showCloseButton && (
|
||||
<button
|
||||
type='button'
|
||||
className='close'
|
||||
onClick={onHideCallback}
|
||||
aria-label={intl.formatMessage({id: 'generic_modal.close', defaultMessage: 'Close'})}
|
||||
>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
<span className='sr-only'>
|
||||
<FormattedMessage id='generic_modal.close' defaultMessage='Close' />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</Modal.Header>
|
||||
)}
|
||||
<Modal.Body className={classNames({divider: bodyDivider, 'overflow-visible': bodyOverflowVisible})}>
|
||||
|
||||
Ссылка в новой задаче
Block a user