mirror of
https://github.com/element-hq/element-web
synced 2024-11-28 12:28:50 +03:00
Merge pull request #3007 from matrix-org/travis/sr/mute-timeline
Make the timeline less noisy for screen readers
This commit is contained in:
commit
983214f4bf
5 changed files with 34 additions and 11 deletions
|
@ -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";
|
||||||
|
|
||||||
|
|
|
@ -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}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue