Merge pull request #3563 from matrix-org/t3chguy/timeline_a11y
Accessibility Improvements
This commit is contained in:
commit
3c14aed534
9 changed files with 75 additions and 25 deletions
|
@ -114,6 +114,15 @@ limitations under the License.
|
||||||
cursor: pointer;
|
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 {
|
.mx_EmojiPicker_search_icon::after {
|
||||||
mask: url('$(res)/img/emojipicker/search.svg') no-repeat;
|
mask: url('$(res)/img/emojipicker/search.svg') no-repeat;
|
||||||
mask-size: 100%;
|
mask-size: 100%;
|
||||||
|
|
|
@ -116,6 +116,21 @@ const LeftPanel = createReactClass({
|
||||||
this.focusedElement = null;
|
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) {
|
_onKeyDown: function(ev) {
|
||||||
if (!this.focusedElement) return;
|
if (!this.focusedElement) return;
|
||||||
|
|
||||||
|
@ -255,6 +270,7 @@ const LeftPanel = createReactClass({
|
||||||
enableRoomSearchFocus={true}
|
enableRoomSearchFocus={true}
|
||||||
blurredPlaceholder={ _t('Filter') }
|
blurredPlaceholder={ _t('Filter') }
|
||||||
placeholder={ _t('Filter rooms…') }
|
placeholder={ _t('Filter rooms…') }
|
||||||
|
onKeyDown={this._onFilterKeyDown}
|
||||||
onSearch={ this.onSearch }
|
onSearch={ this.onSearch }
|
||||||
onCleared={ this.onSearchCleared }
|
onCleared={ this.onSearchCleared }
|
||||||
onFocus={this._onSearchFocus}
|
onFocus={this._onSearchFocus}
|
||||||
|
@ -273,18 +289,19 @@ const LeftPanel = createReactClass({
|
||||||
<TopLeftMenuButton collapsed={this.props.collapsed} />
|
<TopLeftMenuButton collapsed={this.props.collapsed} />
|
||||||
{ breadcrumbs }
|
{ breadcrumbs }
|
||||||
<CallPreview ConferenceHandler={VectorConferenceHandler} />
|
<CallPreview ConferenceHandler={VectorConferenceHandler} />
|
||||||
<div className="mx_LeftPanel_Rooms" onKeyDown={this._onKeyDown} onFocus={this._onFocus} onBlur={this._onBlur}>
|
<div className="mx_LeftPanel_exploreAndFilterRow" onKeyDown={this._onKeyDown} onFocus={this._onFocus} onBlur={this._onBlur}>
|
||||||
<div className="mx_LeftPanel_exploreAndFilterRow">
|
|
||||||
{ exploreButton }
|
{ exploreButton }
|
||||||
{ searchBox }
|
{ searchBox }
|
||||||
</div>
|
</div>
|
||||||
<RoomList
|
<RoomList
|
||||||
|
onKeyDown={this._onKeyDown}
|
||||||
|
onFocus={this._onFocus}
|
||||||
|
onBlur={this._onBlur}
|
||||||
ref={this.collectRoomList}
|
ref={this.collectRoomList}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
collapsed={this.props.collapsed}
|
collapsed={this.props.collapsed}
|
||||||
searchFilter={this.state.searchFilter}
|
searchFilter={this.state.searchFilter}
|
||||||
ConferenceHandler={VectorConferenceHandler} />
|
ConferenceHandler={VectorConferenceHandler} />
|
||||||
</div>
|
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,6 +30,7 @@ module.exports = createReactClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onSearch: PropTypes.func,
|
onSearch: PropTypes.func,
|
||||||
onCleared: PropTypes.func,
|
onCleared: PropTypes.func,
|
||||||
|
onKeyDown: PropTypes.func,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
placeholder: PropTypes.string.isRequired,
|
placeholder: PropTypes.string.isRequired,
|
||||||
|
|
||||||
|
@ -93,6 +94,7 @@ module.exports = createReactClass({
|
||||||
this._clearSearch("keyboard");
|
this._clearSearch("keyboard");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (this.props.onKeyDown) this.props.onKeyDown(ev);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onFocus: function(ev) {
|
_onFocus: function(ev) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ class ReactionPicker extends React.Component {
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
closeMenu: PropTypes.func.isRequired,
|
closeMenu: PropTypes.func.isRequired,
|
||||||
reactions: PropTypes.object.isRequired,
|
reactions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|
|
@ -35,13 +35,22 @@ class Search extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
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 (
|
return (
|
||||||
<div className="mx_EmojiPicker_search">
|
<div className="mx_EmojiPicker_search">
|
||||||
<input autoFocus type="text" placeholder="Search" value={this.props.query}
|
<input autoFocus type="text" placeholder="Search" value={this.props.query}
|
||||||
onChange={ev => this.props.onChange(ev.target.value)} ref={this.inputRef} />
|
onChange={ev => this.props.onChange(ev.target.value)} ref={this.inputRef} />
|
||||||
<button onClick={() => this.props.onChange("")}
|
{rightButton}
|
||||||
className={`mx_EmojiPicker_search_icon ${this.props.query ? "mx_EmojiPicker_search_clear" : ""}`}
|
|
||||||
title={this.props.query ? _t("Cancel search") : _t("Search")} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,11 @@ export default class DateSeparator extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
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>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -770,9 +770,17 @@ module.exports = createReactClass({
|
||||||
|
|
||||||
const subListComponents = this._mapSubListProps(subLists);
|
const subListComponents = this._mapSubListProps(subLists);
|
||||||
|
|
||||||
|
const {resizeNotifier, collapsed, searchFilter, ConferenceHandler, ...props} = this.props; // eslint-disable-line
|
||||||
return (
|
return (
|
||||||
<div ref={this._collectResizeContainer} className="mx_RoomList" role="tree" aria-label={_t("Rooms")}
|
<div
|
||||||
onMouseMove={this.onMouseMove} onMouseLeave={this.onMouseLeave}>
|
{...props}
|
||||||
|
ref={this._collectResizeContainer}
|
||||||
|
className="mx_RoomList"
|
||||||
|
role="tree"
|
||||||
|
aria-label={_t("Rooms")}
|
||||||
|
onMouseMove={this.onMouseMove}
|
||||||
|
onMouseLeave={this.onMouseLeave}
|
||||||
|
>
|
||||||
{ subListComponents }
|
{ subListComponents }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue