Implement Tabs with Automatic Activation pattern for EmojiPicker header
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>pull/21833/head
parent
457f4c82db
commit
6677a3f20e
|
@ -147,8 +147,12 @@ class EmojiPicker extends React.Component {
|
|||
// We update this here instead of through React to avoid re-render on scroll.
|
||||
if (cat.visible) {
|
||||
cat.ref.current.classList.add("mx_EmojiPicker_anchor_visible");
|
||||
cat.ref.current.setAttribute("aria-selected", true);
|
||||
cat.ref.current.setAttribute("tabindex", 0);
|
||||
} else {
|
||||
cat.ref.current.classList.remove("mx_EmojiPicker_anchor_visible");
|
||||
cat.ref.current.setAttribute("aria-selected", false);
|
||||
cat.ref.current.setAttribute("tabindex", -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,20 +18,74 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import classNames from "classnames";
|
||||
|
||||
import {_t} from "../../../languageHandler";
|
||||
import {Key} from "../../../Keyboard";
|
||||
|
||||
class Header extends React.PureComponent {
|
||||
static propTypes = {
|
||||
categories: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onAnchorClick: PropTypes.func.isRequired,
|
||||
refs: PropTypes.object,
|
||||
};
|
||||
|
||||
findNearestEnabled(index, delta) {
|
||||
index += this.props.categories.length;
|
||||
const cats = [...this.props.categories, ...this.props.categories, ...this.props.categories];
|
||||
|
||||
while (index < cats.length && index >= 0) {
|
||||
if (cats[index].enabled) return index % this.props.categories.length;
|
||||
index += delta > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
changeCategoryRelative(delta) {
|
||||
const current = this.props.categories.findIndex(c => c.visible);
|
||||
this.changeCategoryAbsolute(current + delta, delta);
|
||||
}
|
||||
|
||||
changeCategoryAbsolute(index, delta=1) {
|
||||
const category = this.props.categories[this.findNearestEnabled(index, delta)];
|
||||
if (category) {
|
||||
this.props.onAnchorClick(category.id);
|
||||
category.ref.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Implements ARIA Tabs with Automatic Activation pattern
|
||||
// https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-1/tabs.html
|
||||
onKeyDown = (ev) => {
|
||||
let handled = true;
|
||||
switch (ev.key) {
|
||||
case Key.ARROW_LEFT:
|
||||
this.changeCategoryRelative(-1);
|
||||
break;
|
||||
case Key.ARROW_RIGHT:
|
||||
this.changeCategoryRelative(1);
|
||||
break;
|
||||
|
||||
case Key.HOME:
|
||||
this.changeCategoryAbsolute(0);
|
||||
break;
|
||||
case Key.END:
|
||||
this.changeCategoryAbsolute(this.props.categories.length - 1, -1);
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<nav className="mx_EmojiPicker_header" role="tablist">
|
||||
<nav className="mx_EmojiPicker_header" role="tablist" aria-label={_t("Categories")} onKeyDown={this.onKeyDown}>
|
||||
{this.props.categories.map(category => {
|
||||
const classes = classNames(`mx_EmojiPicker_anchor mx_EmojiPicker_anchor_${category.id}`, {
|
||||
mx_EmojiPicker_anchor_visible: category.visible,
|
||||
});
|
||||
// Properties of this button are also modified by EmojiPicker's updateVisibility in DOM.
|
||||
return <button
|
||||
disabled={!category.enabled}
|
||||
key={category.id}
|
||||
|
@ -40,6 +94,7 @@ class Header extends React.PureComponent {
|
|||
onClick={() => this.props.onAnchorClick(category.id)}
|
||||
title={category.name}
|
||||
role="tab"
|
||||
tabIndex={category.visible ? 0 : -1} // roving
|
||||
aria-selected={category.visible}
|
||||
aria-controls={`mx_EmojiPicker_category_${category.id}`}
|
||||
/>;
|
||||
|
|
|
@ -1408,6 +1408,7 @@
|
|||
"Objects": "Objects",
|
||||
"Symbols": "Symbols",
|
||||
"Flags": "Flags",
|
||||
"Categories": "Categories",
|
||||
"Quick Reactions": "Quick Reactions",
|
||||
"Cancel search": "Cancel search",
|
||||
"Unknown Address": "Unknown Address",
|
||||
|
|
Loading…
Reference in New Issue