Various ARIA a11y fixes.

Notate RightPanel tabs.
Shorten Screen Reader queues.
Make AccessibleTooltipButton screen reader friendly
Flatten DOM for Sticker button using React Fragments

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2019-10-03 09:35:39 +01:00
parent b94c94db04
commit f1db0cf027
16 changed files with 62 additions and 57 deletions

View file

@ -126,11 +126,12 @@ const FilePanel = createReactClass({
tileShape="file_grid"
resizeNotifier={this.props.resizeNotifier}
empty={_t('There are no visible files in this room')}
role="tabpanel"
/>
);
} else {
return (
<div className="mx_FilePanel">
<div className="mx_FilePanel" role="tabpanel">
<Loader />
</div>
);

View file

@ -46,12 +46,13 @@ const NotificationPanel = createReactClass({
showUrlPreview={false}
tileShape="notif"
empty={_t('You have no visible notifications')}
role="tabpanel"
/>
);
} else {
console.error("No notifTimelineSet available!");
return (
<div className="mx_NotificationPanel">
<div className="mx_NotificationPanel" role="tabpanel">
<Loader />
</div>
);

View file

@ -258,7 +258,7 @@ const RoomSubList = createReactClass({
const tabindex = this.props.isFiltered ? "0" : "-1";
return (
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
<AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}>
<AccessibleButton onClick={this.onClick} className="mx_RoomSubList_label" tabIndex={tabindex} aria-expanded={!isCollapsed}>
{ chevron }
<span>{this.props.label}</span>
{ incomingCall }

View file

@ -1,18 +1,19 @@
/*
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
@ -55,7 +56,7 @@ export default class AccessibleTooltipButton extends React.PureComponent {
label={title}
/> : <div />;
return (
<AccessibleButton {...props} onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
<AccessibleButton {...props} onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut} aria-label={title}>
{ tip }
</AccessibleButton>
);

View file

@ -183,7 +183,7 @@ module.exports = createReactClass({
const GeminiScrollbarWrapper = sdk.getComponent('elements.GeminiScrollbarWrapper');
return (
<div className="mx_MemberInfo">
<div className="mx_MemberInfo" role="tabpanel">
<GeminiScrollbarWrapper autoshow={true}>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />

View file

@ -222,7 +222,7 @@ export default createReactClass({
}
return (
<div className="mx_MemberList">
<div className="mx_MemberList" role="tabpanel">
{ inviteButton }
<GeminiScrollbarWrapper autoshow={true}>
{ joined }

View file

@ -214,7 +214,7 @@ module.exports = createReactClass({
const groupRoomName = this.state.groupRoom.displayname;
return (
<div className="mx_MemberInfo">
<div className="mx_MemberInfo" role="tabpanel">
<GeminiScrollbarWrapper autoshow={true}>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />

View file

@ -153,7 +153,7 @@ export default createReactClass({
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
const TruncatedList = sdk.getComponent("elements.TruncatedList");
return (
<div className="mx_GroupRoomList">
<div className="mx_GroupRoomList" role="tabpanel">
{ inviteButton }
<GeminiScrollbarWrapper autoshow={true} className="mx_GroupRoomList_joined mx_GroupRoomList_outerWrapper">
<TruncatedList className="mx_GroupRoomList_wrapper" truncateAt={this.state.truncateAt}

View file

@ -3,6 +3,7 @@ Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Copyright 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -42,8 +43,8 @@ export default class HeaderButton extends React.Component {
});
return <AccessibleButton
aria-label={this.props.title}
aria-expanded={this.props.isHighlighted}
aria-selected={this.props.isHighlighted}
role="tab"
title={this.props.title}
className={classes}
onClick={this.onClick}>

View file

@ -91,7 +91,7 @@ export default class HeaderButtons extends React.Component {
render() {
// inline style as this will be swapped around in future commits
return <div className="mx_HeaderButtons">
return <div className="mx_HeaderButtons" role="tablist">
{ this.renderButtons() }
</div>;
}

View file

@ -1124,35 +1124,35 @@ module.exports = createReactClass({
}
return (
<div className="mx_MemberInfo">
<div className="mx_MemberInfo_name">
{ backButton }
{ e2eIconElement }
<h2>{ memberName }</h2>
<div className="mx_MemberInfo" role="tabpanel">
<div className="mx_MemberInfo_name">
{ backButton }
{ e2eIconElement }
<h2>{ memberName }</h2>
</div>
{ avatarElement }
<div className="mx_MemberInfo_container">
<div className="mx_MemberInfo_profile">
<div className="mx_MemberInfo_profileField">
{ this.props.member.userId }
</div>
{ roomMemberDetails }
</div>
{ avatarElement }
</div>
<AutoHideScrollbar className="mx_MemberInfo_scrollContainer">
<div className="mx_MemberInfo_container">
{ this._renderUserOptions() }
<div className="mx_MemberInfo_profile">
<div className="mx_MemberInfo_profileField">
{ this.props.member.userId }
</div>
{ roomMemberDetails }
</div>
{ adminTools }
{ startChat }
{ this._renderDevices() }
{ spinner }
</div>
<AutoHideScrollbar className="mx_MemberInfo_scrollContainer">
<div className="mx_MemberInfo_container">
{ this._renderUserOptions() }
{ adminTools }
{ startChat }
{ this._renderDevices() }
{ spinner }
</div>
</AutoHideScrollbar>
</AutoHideScrollbar>
</div>
);
},

View file

@ -475,7 +475,7 @@ module.exports = createReactClass({
}
return (
<div className="mx_MemberList">
<div className="mx_MemberList" role="tabpanel">
{ inviteButton }
<AutoHideScrollbar>
<div className="mx_MemberList_wrapper">

View file

@ -382,14 +382,15 @@ module.exports = createReactClass({
/>;
}
// The following labels are written in such a fashion to increase screen reader efficiency (speed).
if (notifBadges && mentionBadges && !isInvite) {
ariaLabel += " " + _t("It has %(count)s unread messages including mentions.", {
ariaLabel += " " + _t("%(count)s unread messages including mentions.", {
count: notificationCount,
});
} else if (notifBadges) {
ariaLabel += " " + _t("It has %(count)s unread messages.", { count: notificationCount });
ariaLabel += " " + _t("%(count)s unread messages.", { count: notificationCount });
} else if (mentionBadges && !isInvite) {
ariaLabel += " " + _t("It has unread mentions.");
ariaLabel += " " + _t("Unread mentions.");
}
return <AccessibleButton tabIndex="0"

View file

@ -409,9 +409,9 @@ export default class Stickerpicker extends React.Component {
>
</AccessibleButton>;
}
return <div>
return <React.Fragment>
{stickersButton}
{this.state.showStickers && stickerPicker}
</div>;
</React.Fragment>;
}
}

View file

@ -121,7 +121,7 @@ export default class ThirdPartyMemberInfo extends React.Component {
// We shamelessly rip off the MemberInfo styles here.
return (
<div className="mx_MemberInfo">
<div className="mx_MemberInfo" role="tabpanel">
<div className="mx_MemberInfo_name">
<AccessibleButton className="mx_MemberInfo_cancel"
onClick={this.onCancel}

View file

@ -950,9 +950,9 @@
"Securely back up your keys to avoid losing them. <a>Learn more.</a>": "Securely back up your keys to avoid losing them. <a>Learn more.</a>",
"Not now": "Not now",
"Don't ask me again": "Don't ask me again",
"It has %(count)s unread messages including mentions.|other": "It has %(count)s unread messages including mentions.",
"It has %(count)s unread messages.|other": "It has %(count)s unread messages.",
"It has unread mentions.": "It has unread mentions.",
"%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.",
"%(count)s unread messages.|other": "%(count)s unread messages.",
"Unread mentions.": "Unread mentions.",
"Add a topic": "Add a topic",
"Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.",
"This room has already been upgraded.": "This room has already been upgraded.",