Merge pull request #3563 from matrix-org/t3chguy/timeline_a11y

Accessibility Improvements
This commit is contained in:
Travis Ralston 2019-10-23 13:37:04 -06:00 committed by GitHub
commit 3c14aed534
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 75 additions and 25 deletions

View file

@ -114,6 +114,15 @@ limitations under the License.
cursor: pointer;
}
.mx_EmojiPicker_search_icon {
width: 16px;
margin: 8px;
}
.mx_EmojiPicker_search_icon:not(.mx_EmojiPicker_search_clear) {
pointer-events: none;
}
.mx_EmojiPicker_search_icon::after {
mask: url('$(res)/img/emojipicker/search.svg') no-repeat;
mask-size: 100%;

View file

@ -116,6 +116,21 @@ const LeftPanel = createReactClass({
this.focusedElement = null;
},
_onFilterKeyDown: function(ev) {
if (!this.focusedElement) return;
switch (ev.key) {
// On enter of rooms filter select and activate first room if such one exists
case Key.ENTER: {
const firstRoom = ev.target.closest(".mx_LeftPanel").querySelector(".mx_RoomTile");
if (firstRoom) {
firstRoom.click();
}
break;
}
}
},
_onKeyDown: function(ev) {
if (!this.focusedElement) return;
@ -255,6 +270,7 @@ const LeftPanel = createReactClass({
enableRoomSearchFocus={true}
blurredPlaceholder={ _t('Filter') }
placeholder={ _t('Filter rooms…') }
onKeyDown={this._onFilterKeyDown}
onSearch={ this.onSearch }
onCleared={ this.onSearchCleared }
onFocus={this._onSearchFocus}
@ -273,18 +289,19 @@ const LeftPanel = createReactClass({
<TopLeftMenuButton collapsed={this.props.collapsed} />
{ breadcrumbs }
<CallPreview ConferenceHandler={VectorConferenceHandler} />
<div className="mx_LeftPanel_Rooms" onKeyDown={this._onKeyDown} onFocus={this._onFocus} onBlur={this._onBlur}>
<div className="mx_LeftPanel_exploreAndFilterRow">
<div className="mx_LeftPanel_exploreAndFilterRow" onKeyDown={this._onKeyDown} onFocus={this._onFocus} onBlur={this._onBlur}>
{ exploreButton }
{ searchBox }
</div>
<RoomList
onKeyDown={this._onKeyDown}
onFocus={this._onFocus}
onBlur={this._onBlur}
ref={this.collectRoomList}
resizeNotifier={this.props.resizeNotifier}
collapsed={this.props.collapsed}
searchFilter={this.state.searchFilter}
ConferenceHandler={VectorConferenceHandler} />
</div>
</aside>
</div>
);

View file

@ -30,6 +30,7 @@ module.exports = createReactClass({
propTypes: {
onSearch: PropTypes.func,
onCleared: PropTypes.func,
onKeyDown: PropTypes.func,
className: PropTypes.string,
placeholder: PropTypes.string.isRequired,
@ -93,6 +94,7 @@ module.exports = createReactClass({
this._clearSearch("keyboard");
break;
}
if (this.props.onKeyDown) this.props.onKeyDown(ev);
},
_onFocus: function(ev) {

View file

@ -24,7 +24,7 @@ class ReactionPicker extends React.Component {
mxEvent: PropTypes.object.isRequired,
onFinished: PropTypes.func.isRequired,
closeMenu: PropTypes.func.isRequired,
reactions: PropTypes.object.isRequired,
reactions: PropTypes.object,
};
constructor(props) {

View file

@ -35,13 +35,22 @@ class Search extends React.PureComponent {
}
render() {
let rightButton;
if (this.props.query) {
rightButton = (
<button onClick={() => this.props.onChange("")}
className="mx_EmojiPicker_search_icon mx_EmojiPicker_search_clear"
title={_t("Cancel search")} />
);
} else {
rightButton = <span className="mx_EmojiPicker_search_icon" />;
}
return (
<div className="mx_EmojiPicker_search">
<input autoFocus type="text" placeholder="Search" value={this.props.query}
onChange={ev => this.props.onChange(ev.target.value)} ref={this.inputRef} />
<button onClick={() => this.props.onChange("")}
className={`mx_EmojiPicker_search_icon ${this.props.query ? "mx_EmojiPicker_search_clear" : ""}`}
title={this.props.query ? _t("Cancel search") : _t("Search")} />
{rightButton}
</div>
);
}

View file

@ -56,6 +56,11 @@ export default class DateSeparator extends React.Component {
}
render() {
return <h2 className="mx_DateSeparator"><hr /><div>{ this.getLabel() }</div><hr /></h2>;
// ARIA treats <hr/>s as separators, here we abuse them slightly so manually treat this entire thing as one
return <h2 className="mx_DateSeparator" role="separator">
<hr role="none" />
<div>{ this.getLabel() }</div>
<hr role="none" />
</h2>;
}
}

View file

@ -705,7 +705,7 @@ module.exports = createReactClass({
{ timestamp }
</a>
</div>
<div className="mx_EventTile_line" >
<div className="mx_EventTile_line">
<EventTileType ref="tile"
mxEvent={this.props.mxEvent}
highlights={this.props.highlights}
@ -719,7 +719,7 @@ module.exports = createReactClass({
case 'file_grid': {
return (
<div className={classes}>
<div className="mx_EventTile_line" >
<div className="mx_EventTile_line">
<EventTileType ref="tile"
mxEvent={this.props.mxEvent}
highlights={this.props.highlights}

View file

@ -770,9 +770,17 @@ module.exports = createReactClass({
const subListComponents = this._mapSubListProps(subLists);
const {resizeNotifier, collapsed, searchFilter, ConferenceHandler, ...props} = this.props; // eslint-disable-line
return (
<div ref={this._collectResizeContainer} className="mx_RoomList" role="tree" aria-label={_t("Rooms")}
onMouseMove={this.onMouseMove} onMouseLeave={this.onMouseLeave}>
<div
{...props}
ref={this._collectResizeContainer}
className="mx_RoomList"
role="tree"
aria-label={_t("Rooms")}
onMouseMove={this.onMouseMove}
onMouseLeave={this.onMouseLeave}
>
{ subListComponents }
</div>
);