Accessible emoiji picker

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak
2021-07-03 15:28:55 +02:00
parent 8f53134b5e
commit 4d3f4c5680
6 changed files with 117 additions and 8 deletions

View File

@@ -15,25 +15,64 @@ class EmojiSelector extends ImmutablePureComponent {
static propTypes = {
onReact: PropTypes.func.isRequired,
onUnfocus: PropTypes.func,
visible: PropTypes.bool,
focused: PropTypes.bool,
}
static defaultProps = {
onReact: () => {},
onUnfocus: () => {},
visible: false,
}
handleBlur = e => {
const { focused, onUnfocus } = this.props;
if (focused && (!e.relatedTarget || !e.relatedTarget.classList.contains('emoji-react-selector__emoji'))) {
onUnfocus();
}
}
handleKeyUp = i => e => {
switch (e.key) {
case 'Left':
case 'ArrowLeft':
if (i !== 0) {
this.node.querySelector(`.emoji-react-selector__emoji:nth-child(${i})`).focus();
}
break;
case 'Right':
case 'ArrowRight':
if (i !== this.props.allowedEmoji.size - 1) {
this.node.querySelector(`.emoji-react-selector__emoji:nth-child(${i + 2})`).focus();
}
break;
}
}
setRef = c => {
this.node = c;
}
render() {
const { onReact, visible, allowedEmoji } = this.props;
const { onReact, visible, focused, allowedEmoji } = this.props;
return (
<div className={classNames('emoji-react-selector', { 'emoji-react-selector--visible': visible })}>
<div
className={classNames('emoji-react-selector', { 'emoji-react-selector--visible': visible, 'emoji-react-selector--focused': focused })}
onBlur={this.handleBlur}
ref={this.setRef}
>
{allowedEmoji.map((emoji, i) => (
<button
key={i}
className='emoji-react-selector__emoji'
dangerouslySetInnerHTML={{ __html: emojify(emoji) }}
onClick={onReact(emoji)}
onKeyUp={this.handleKeyUp(i)}
tabIndex={(visible || focused) ? 0 : -1}
/>
))}
</div>

View File

@@ -13,6 +13,8 @@ export default class IconButton extends React.PureComponent {
title: PropTypes.string.isRequired,
icon: PropTypes.string.isRequired,
onClick: PropTypes.func,
onKeyUp: PropTypes.func,
onKeyDown: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
size: PropTypes.number,
@@ -37,6 +39,8 @@ export default class IconButton extends React.PureComponent {
animate: false,
overlay: false,
tabIndex: '0',
onKeyUp: () => {},
onKeyDown: () => {},
onClick: () => {},
onMouseEnter: () => {},
onMouseLeave: () => {},
@@ -94,6 +98,8 @@ export default class IconButton extends React.PureComponent {
title={title}
className={classes}
onClick={this.handleClick}
onKeyUp={this.props.onKeyUp}
onKeyDown={this.props.onKeyDown}
onMouseEnter={this.props.onMouseEnter}
onMouseLeave={this.props.onMouseLeave}
tabIndex={tabIndex}
@@ -119,6 +125,8 @@ export default class IconButton extends React.PureComponent {
title={title}
className={classes}
onClick={this.handleClick}
onKeyUp={this.props.onKeyUp}
onKeyDown={this.props.onKeyDown}
onMouseEnter={this.props.onMouseEnter}
onMouseLeave={this.props.onMouseLeave}
tabIndex={tabIndex}