Merge pull request #3007 from matrix-org/travis/sr/mute-timeline

Make the timeline less noisy for screen readers
This commit is contained in:
Travis Ralston 2019-05-22 09:55:23 -06:00 committed by GitHub
commit 983214f4bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 34 additions and 11 deletions

View file

@ -67,8 +67,8 @@ export default function AccessibleButton(props) {
restProps.ref = restProps.inputRef; restProps.ref = restProps.inputRef;
delete restProps.inputRef; delete restProps.inputRef;
restProps.tabIndex = restProps.tabIndex || "0"; restProps.tabIndex = restProps.tabIndex === undefined ? "0" : restProps.tabIndex;
restProps.role = "button"; restProps.role = restProps.role === undefined ? "button" : restProps.role;
restProps.className = (restProps.className ? restProps.className + " " : "") + restProps.className = (restProps.className ? restProps.className + " " : "") +
"mx_AccessibleButton"; "mx_AccessibleButton";

View file

@ -45,12 +45,18 @@ class FlairAvatar extends React.Component {
const tooltip = this.props.groupProfile.name ? const tooltip = this.props.groupProfile.name ?
`${this.props.groupProfile.name} (${this.props.groupProfile.groupId})`: `${this.props.groupProfile.name} (${this.props.groupProfile.groupId})`:
this.props.groupProfile.groupId; this.props.groupProfile.groupId;
// Note: we hide flair from screen readers but ideally we'd support
// reading something out on hover. There's no easy way to do this though,
// so instead we just hide it completely.
return <img return <img
src={httpUrl} src={httpUrl}
width="16" width="16"
height="16" height="16"
onClick={this.onClick} onClick={this.onClick}
title={tooltip} />; title={tooltip}
aria-hidden={true}
/>;
} }
} }

View file

@ -23,12 +23,17 @@ export default class MessageTimestamp extends React.Component {
static propTypes = { static propTypes = {
ts: PropTypes.number.isRequired, ts: PropTypes.number.isRequired,
showTwelveHour: PropTypes.bool, showTwelveHour: PropTypes.bool,
ariaHidden: PropTypes.bool,
}; };
render() { render() {
const date = new Date(this.props.ts); const date = new Date(this.props.ts);
return ( return (
<span className="mx_MessageTimestamp" title={formatFullDate(date, this.props.showTwelveHour)}> <span
className="mx_MessageTimestamp"
title={formatFullDate(date, this.props.showTwelveHour)}
aria-hidden={this.props.ariaHidden}
>
{ formatTime(date, this.props.showTwelveHour) } { formatTime(date, this.props.showTwelveHour) }
</span> </span>
); );

View file

@ -545,6 +545,8 @@ module.exports = withMatrixClient(React.createClass({
const isRedacted = isMessageEvent(this.props.mxEvent) && this.props.isRedacted; const isRedacted = isMessageEvent(this.props.mxEvent) && this.props.isRedacted;
const isEncryptionFailure = this.props.mxEvent.isDecryptionFailure(); const isEncryptionFailure = this.props.mxEvent.isDecryptionFailure();
const muteScreenReader = isSending || !this.props.eventSendStatus;
const classes = classNames({ const classes = classNames({
mx_EventTile: true, mx_EventTile: true,
mx_EventTile_isEditing: this.props.isEditing, mx_EventTile_isEditing: this.props.isEditing,
@ -601,9 +603,13 @@ module.exports = withMatrixClient(React.createClass({
if (this.props.mxEvent.sender && avatarSize) { if (this.props.mxEvent.sender && avatarSize) {
avatar = ( avatar = (
<div className="mx_EventTile_avatar"> <div className="mx_EventTile_avatar">
<MemberAvatar member={this.props.mxEvent.sender} <MemberAvatar
member={this.props.mxEvent.sender}
width={avatarSize} height={avatarSize} width={avatarSize} height={avatarSize}
viewUserOnClick={true} viewUserOnClick={true}
aria-hidden={true} /* silence screen readers */
buttonRole={null} /* trick screen readers into thinking this is not a button */
tabIndex={null} /* trick screen readers into thinking this is not a button */
/> />
</div> </div>
); );
@ -634,8 +640,12 @@ module.exports = withMatrixClient(React.createClass({
onFocusChange={this.onActionBarFocusChange} onFocusChange={this.onActionBarFocusChange}
/> : undefined; /> : undefined;
const timestamp = this.props.mxEvent.getTs() ? const timestamp = this.props.mxEvent.getTs()
<MessageTimestamp showTwelveHour={this.props.isTwelveHour} ts={this.props.mxEvent.getTs()} /> : null; ? <MessageTimestamp
showTwelveHour={this.props.isTwelveHour}
ts={this.props.mxEvent.getTs()}
ariaHidden={muteScreenReader}
/> : null;
const keyRequestHelpText = const keyRequestHelpText =
<div className="mx_EventTile_keyRequestInfo_tooltip_contents"> <div className="mx_EventTile_keyRequestInfo_tooltip_contents">
@ -773,13 +783,13 @@ module.exports = withMatrixClient(React.createClass({
'replyThread', 'replyThread',
); );
return ( return (
<div className={classes}> <div className={classes} aria-hidden={muteScreenReader}>
<div className="mx_EventTile_msgOption"> <div className="mx_EventTile_msgOption">
{ readAvatars } { readAvatars }
</div> </div>
{ sender } { sender }
<div className="mx_EventTile_line"> <div className="mx_EventTile_line">
<a href={permalink} onClick={this.onPermalinkClicked}> <a href={permalink} onClick={this.onPermalinkClicked} aria-hidden={muteScreenReader}>
{ timestamp } { timestamp }
</a> </a>
{ this._renderE2EPadlock() } { this._renderE2EPadlock() }
@ -797,7 +807,7 @@ module.exports = withMatrixClient(React.createClass({
{ actionBar } { actionBar }
</div> </div>
{ {
// The avatar goes after the event tile as it's absolutly positioned to be over the // The avatar goes after the event tile as it's absolutely positioned to be over the
// event tile line, so needs to be later in the DOM so it appears on top (this avoids // event tile line, so needs to be later in the DOM so it appears on top (this avoids
// the need for further z-indexing chaos) // the need for further z-indexing chaos)
} }

View file

@ -211,11 +211,13 @@ module.exports = React.createClass({
<MemberAvatar <MemberAvatar
member={this.props.member} member={this.props.member}
fallbackUserId={this.props.fallbackUserId} fallbackUserId={this.props.fallbackUserId}
aria-hidden="true"
width={14} height={14} resizeMethod="crop" width={14} height={14} resizeMethod="crop"
style={style} style={style}
title={title} title={title}
onClick={this.props.onClick} onClick={this.props.onClick}
aria-hidden={true} /* silence screen readers */
buttonRole={null} /* trick screen readers into thinking this is not a button */
tabIndex={null} /* trick screen readers into thinking this is not a button */
/> />
</Velociraptor> </Velociraptor>
); );