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:
parent
b94c94db04
commit
f1db0cf027
16 changed files with 62 additions and 57 deletions
|
@ -126,11 +126,12 @@ const FilePanel = createReactClass({
|
||||||
tileShape="file_grid"
|
tileShape="file_grid"
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
empty={_t('There are no visible files in this room')}
|
empty={_t('There are no visible files in this room')}
|
||||||
|
role="tabpanel"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div className="mx_FilePanel">
|
<div className="mx_FilePanel" role="tabpanel">
|
||||||
<Loader />
|
<Loader />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -46,12 +46,13 @@ const NotificationPanel = createReactClass({
|
||||||
showUrlPreview={false}
|
showUrlPreview={false}
|
||||||
tileShape="notif"
|
tileShape="notif"
|
||||||
empty={_t('You have no visible notifications')}
|
empty={_t('You have no visible notifications')}
|
||||||
|
role="tabpanel"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.error("No notifTimelineSet available!");
|
console.error("No notifTimelineSet available!");
|
||||||
return (
|
return (
|
||||||
<div className="mx_NotificationPanel">
|
<div className="mx_NotificationPanel" role="tabpanel">
|
||||||
<Loader />
|
<Loader />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -258,7 +258,7 @@ const RoomSubList = createReactClass({
|
||||||
const tabindex = this.props.isFiltered ? "0" : "-1";
|
const tabindex = this.props.isFiltered ? "0" : "-1";
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
|
<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 }
|
{ chevron }
|
||||||
<span>{this.props.label}</span>
|
<span>{this.props.label}</span>
|
||||||
{ incomingCall }
|
{ incomingCall }
|
||||||
|
|
|
@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
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
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
@ -55,7 +56,7 @@ export default class AccessibleTooltipButton extends React.PureComponent {
|
||||||
label={title}
|
label={title}
|
||||||
/> : <div />;
|
/> : <div />;
|
||||||
return (
|
return (
|
||||||
<AccessibleButton {...props} onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
|
<AccessibleButton {...props} onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut} aria-label={title}>
|
||||||
{ tip }
|
{ tip }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
|
|
|
@ -183,7 +183,7 @@ module.exports = createReactClass({
|
||||||
|
|
||||||
const GeminiScrollbarWrapper = sdk.getComponent('elements.GeminiScrollbarWrapper');
|
const GeminiScrollbarWrapper = sdk.getComponent('elements.GeminiScrollbarWrapper');
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberInfo">
|
<div className="mx_MemberInfo" role="tabpanel">
|
||||||
<GeminiScrollbarWrapper autoshow={true}>
|
<GeminiScrollbarWrapper autoshow={true}>
|
||||||
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
|
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
|
||||||
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
|
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
|
||||||
|
|
|
@ -222,7 +222,7 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberList">
|
<div className="mx_MemberList" role="tabpanel">
|
||||||
{ inviteButton }
|
{ inviteButton }
|
||||||
<GeminiScrollbarWrapper autoshow={true}>
|
<GeminiScrollbarWrapper autoshow={true}>
|
||||||
{ joined }
|
{ joined }
|
||||||
|
|
|
@ -214,7 +214,7 @@ module.exports = createReactClass({
|
||||||
|
|
||||||
const groupRoomName = this.state.groupRoom.displayname;
|
const groupRoomName = this.state.groupRoom.displayname;
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberInfo">
|
<div className="mx_MemberInfo" role="tabpanel">
|
||||||
<GeminiScrollbarWrapper autoshow={true}>
|
<GeminiScrollbarWrapper autoshow={true}>
|
||||||
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
|
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
|
||||||
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
|
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
|
||||||
|
|
|
@ -153,7 +153,7 @@ export default createReactClass({
|
||||||
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
|
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
|
||||||
const TruncatedList = sdk.getComponent("elements.TruncatedList");
|
const TruncatedList = sdk.getComponent("elements.TruncatedList");
|
||||||
return (
|
return (
|
||||||
<div className="mx_GroupRoomList">
|
<div className="mx_GroupRoomList" role="tabpanel">
|
||||||
{ inviteButton }
|
{ inviteButton }
|
||||||
<GeminiScrollbarWrapper autoshow={true} className="mx_GroupRoomList_joined mx_GroupRoomList_outerWrapper">
|
<GeminiScrollbarWrapper autoshow={true} className="mx_GroupRoomList_joined mx_GroupRoomList_outerWrapper">
|
||||||
<TruncatedList className="mx_GroupRoomList_wrapper" truncateAt={this.state.truncateAt}
|
<TruncatedList className="mx_GroupRoomList_wrapper" truncateAt={this.state.truncateAt}
|
||||||
|
|
|
@ -3,6 +3,7 @@ Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2017 New Vector Ltd
|
Copyright 2017 New Vector Ltd
|
||||||
Copyright 2018 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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
|
return <AccessibleButton
|
||||||
aria-label={this.props.title}
|
aria-selected={this.props.isHighlighted}
|
||||||
aria-expanded={this.props.isHighlighted}
|
role="tab"
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
className={classes}
|
className={classes}
|
||||||
onClick={this.onClick}>
|
onClick={this.onClick}>
|
||||||
|
|
|
@ -91,7 +91,7 @@ export default class HeaderButtons extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// inline style as this will be swapped around in future commits
|
// 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() }
|
{ this.renderButtons() }
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1124,35 +1124,35 @@ module.exports = createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberInfo">
|
<div className="mx_MemberInfo" role="tabpanel">
|
||||||
<div className="mx_MemberInfo_name">
|
<div className="mx_MemberInfo_name">
|
||||||
{ backButton }
|
{ backButton }
|
||||||
{ e2eIconElement }
|
{ e2eIconElement }
|
||||||
<h2>{ memberName }</h2>
|
<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>
|
</div>
|
||||||
{ avatarElement }
|
</div>
|
||||||
|
<AutoHideScrollbar className="mx_MemberInfo_scrollContainer">
|
||||||
<div className="mx_MemberInfo_container">
|
<div className="mx_MemberInfo_container">
|
||||||
|
{ this._renderUserOptions() }
|
||||||
|
|
||||||
<div className="mx_MemberInfo_profile">
|
{ adminTools }
|
||||||
<div className="mx_MemberInfo_profileField">
|
|
||||||
{ this.props.member.userId }
|
{ startChat }
|
||||||
</div>
|
|
||||||
{ roomMemberDetails }
|
{ this._renderDevices() }
|
||||||
</div>
|
|
||||||
|
{ spinner }
|
||||||
</div>
|
</div>
|
||||||
<AutoHideScrollbar className="mx_MemberInfo_scrollContainer">
|
</AutoHideScrollbar>
|
||||||
<div className="mx_MemberInfo_container">
|
|
||||||
{ this._renderUserOptions() }
|
|
||||||
|
|
||||||
{ adminTools }
|
|
||||||
|
|
||||||
{ startChat }
|
|
||||||
|
|
||||||
{ this._renderDevices() }
|
|
||||||
|
|
||||||
{ spinner }
|
|
||||||
</div>
|
|
||||||
</AutoHideScrollbar>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -475,7 +475,7 @@ module.exports = createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberList">
|
<div className="mx_MemberList" role="tabpanel">
|
||||||
{ inviteButton }
|
{ inviteButton }
|
||||||
<AutoHideScrollbar>
|
<AutoHideScrollbar>
|
||||||
<div className="mx_MemberList_wrapper">
|
<div className="mx_MemberList_wrapper">
|
||||||
|
|
|
@ -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) {
|
if (notifBadges && mentionBadges && !isInvite) {
|
||||||
ariaLabel += " " + _t("It has %(count)s unread messages including mentions.", {
|
ariaLabel += " " + _t("%(count)s unread messages including mentions.", {
|
||||||
count: notificationCount,
|
count: notificationCount,
|
||||||
});
|
});
|
||||||
} else if (notifBadges) {
|
} 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) {
|
} else if (mentionBadges && !isInvite) {
|
||||||
ariaLabel += " " + _t("It has unread mentions.");
|
ariaLabel += " " + _t("Unread mentions.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return <AccessibleButton tabIndex="0"
|
return <AccessibleButton tabIndex="0"
|
||||||
|
|
|
@ -409,9 +409,9 @@ export default class Stickerpicker extends React.Component {
|
||||||
>
|
>
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
}
|
}
|
||||||
return <div>
|
return <React.Fragment>
|
||||||
{stickersButton}
|
{stickersButton}
|
||||||
{this.state.showStickers && stickerPicker}
|
{this.state.showStickers && stickerPicker}
|
||||||
</div>;
|
</React.Fragment>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ export default class ThirdPartyMemberInfo extends React.Component {
|
||||||
|
|
||||||
// We shamelessly rip off the MemberInfo styles here.
|
// We shamelessly rip off the MemberInfo styles here.
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberInfo">
|
<div className="mx_MemberInfo" role="tabpanel">
|
||||||
<div className="mx_MemberInfo_name">
|
<div className="mx_MemberInfo_name">
|
||||||
<AccessibleButton className="mx_MemberInfo_cancel"
|
<AccessibleButton className="mx_MemberInfo_cancel"
|
||||||
onClick={this.onCancel}
|
onClick={this.onCancel}
|
||||||
|
|
|
@ -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>",
|
"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",
|
"Not now": "Not now",
|
||||||
"Don't ask me again": "Don't ask me again",
|
"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.",
|
"%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.",
|
||||||
"It has %(count)s unread messages.|other": "It has %(count)s unread messages.",
|
"%(count)s unread messages.|other": "%(count)s unread messages.",
|
||||||
"It has unread mentions.": "It has unread mentions.",
|
"Unread mentions.": "Unread mentions.",
|
||||||
"Add a topic": "Add a topic",
|
"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.",
|
"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.",
|
"This room has already been upgraded.": "This room has already been upgraded.",
|
||||||
|
|
Loading…
Reference in a new issue