fix focus and tab order bugs

This commit is contained in:
KDSBrowne 2017-05-06 16:04:59 -07:00
parent 569ba42042
commit 8bb1da0ed5
7 changed files with 130 additions and 27 deletions

View File

@ -18,7 +18,94 @@ const propTypes = {
class EmojiMenu extends Component { class EmojiMenu extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
// this.rovingIndex = this.rovingIndex.bind(this);
} }
/*
rovingIndex(...Args) {
const {users, openChats} = this.props;
let active = document.activeElement;
let list;
let items;
let count;
function focusList() {
active.tabIndex = -1;
this.counter = 0;
list.tabIndex = 0;
list.focus();
}
if (Args[1] === 'users'){
list = findDOMNode(this.refs.usersList);
items = findDOMNode(this.refs.userItems);
count = users.length;
}else if(Args[1] === 'messages'){
list = findDOMNode(this.refs.msgList);
items = findDOMNode(this.refs.msgItems);
count = openChats.length;
}
if(this.counter === -1 || this.counter > count){
active.tabIndex = -1;
list.tabIndex = 0;
this.counter = 0;
list.focus();
}
if (Args[0].keyCode === KEY_CODES.ENTER
|| Args[0].keyCode === KEY_CODES.ARROW_RIGHT
|| Args[0].keyCode === KEY_CODES.ARROW_LEFT) {
active.firstChild.click();
}
if (Args[0].keyCode === KEY_CODES.ESCAPE) {
active.tabIndex = -1;
focusList();
}
if (Args[0].keyCode === KEY_CODES.ARROW_DOWN) {
if (this.counter < count) {
active.tabIndex = -1;
items.childNodes[this.counter].tabIndex = 0;
let newFocus = items.childNodes[this.counter];
this.counter++;
newFocus.focus();
}else if(this.counter === count){
active.tabIndex = -1;
this.counter = -1;
list.tabIndex = 0;
list.focus();
}else if(this.counter === 0) {
active.tabIndex = -1;
this.counter = 1;
items.childNodes[this.counter].tabIndex = 0;
let newFocus = items.childNodes[this.counter];
newFocus.focus();
}
}
if (Args[0].keyCode === KEY_CODES.ARROW_UP) {
if (this.counter < count && this.counter !== 0) {
active.tabIndex = -1;
this.counter--;
items.childNodes[this.counter].tabIndex = 0;
let newFocus = items.childNodes[this.counter];
newFocus.focus();
}else if(this.counter === 0){
active.tabIndex = -1;
this.counter = count;
list.tabIndex = 0;
list.focus();
}else if (this.counter === count){
active.tabIndex = -1;
this.counter = count - 1;
items.childNodes[this.counter].tabIndex = 0;
let newFocus = items.childNodes[this.counter];
newFocus.focus();
}
}
} */
render() { render() {
const { const {
@ -29,7 +116,7 @@ class EmojiMenu extends Component {
return ( return (
<Dropdown ref="dropdown" autoFocus={true}> <Dropdown ref="dropdown" autoFocus={true}>
<DropdownTrigger> <DropdownTrigger placeInTabOrder={true}>
<Button <Button
role="button" role="button"
label={intl.formatMessage(intlMessages.statusTriggerLabel)} label={intl.formatMessage(intlMessages.statusTriggerLabel)}
@ -51,60 +138,69 @@ class EmojiMenu extends Component {
</Button> </Button>
</DropdownTrigger> </DropdownTrigger>
<DropdownContent placement="top left"> <DropdownContent placement="top left">
<DropdownList> <DropdownList ref={'dropdown'}>
<DropdownListItem <DropdownListItem
icon="hand" icon="hand"
label={intl.formatMessage(intlMessages.raiseLabel)} label={intl.formatMessage(intlMessages.raiseLabel)}
description={intl.formatMessage(intlMessages.raiseDesc)} description={intl.formatMessage(intlMessages.raiseDesc)}
onClick={() => actions.setEmojiHandler('raiseHand')} onClick={() => actions.setEmojiHandler('raiseHand')}
tabIndex={-1}
/> />
<DropdownListItem <DropdownListItem
icon="happy" icon="happy"
label={intl.formatMessage(intlMessages.happyLabel)} label={intl.formatMessage(intlMessages.happyLabel)}
description={intl.formatMessage(intlMessages.happyDesc)} description={intl.formatMessage(intlMessages.happyDesc)}
onClick={() => actions.setEmojiHandler('happy')} onClick={() => actions.setEmojiHandler('happy')}
tabIndex={-1}
/> />
<DropdownListItem <DropdownListItem
icon="undecided" icon="undecided"
label={intl.formatMessage(intlMessages.undecidedLabel)} label={intl.formatMessage(intlMessages.undecidedLabel)}
description={intl.formatMessage(intlMessages.undecidedDesc)} description={intl.formatMessage(intlMessages.undecidedDesc)}
onClick={() => actions.setEmojiHandler('neutral')} onClick={() => actions.setEmojiHandler('neutral')}
tabIndex={-1}
/> />
<DropdownListItem <DropdownListItem
icon="sad" icon="sad"
label={intl.formatMessage(intlMessages.sadLabel)} label={intl.formatMessage(intlMessages.sadLabel)}
description={intl.formatMessage(intlMessages.sadDesc)} description={intl.formatMessage(intlMessages.sadDesc)}
onClick={() => actions.setEmojiHandler('sad')} onClick={() => actions.setEmojiHandler('sad')}
tabIndex={-1}
/> />
<DropdownListItem <DropdownListItem
icon="confused" icon="confused"
label={intl.formatMessage(intlMessages.confusedLabel)} label={intl.formatMessage(intlMessages.confusedLabel)}
description={intl.formatMessage(intlMessages.confusedDesc)} description={intl.formatMessage(intlMessages.confusedDesc)}
onClick={() => actions.setEmojiHandler('confused')} onClick={() => actions.setEmojiHandler('confused')}
tabIndex={-1}
/> />
<DropdownListItem <DropdownListItem
icon="time" icon="time"
label={intl.formatMessage(intlMessages.awayLabel)} label={intl.formatMessage(intlMessages.awayLabel)}
description={intl.formatMessage(intlMessages.awayDesc)} description={intl.formatMessage(intlMessages.awayDesc)}
onClick={() => actions.setEmojiHandler('away')} onClick={() => actions.setEmojiHandler('away')}
tabIndex={-1}
/> />
<DropdownListItem <DropdownListItem
icon="thumbs_up" icon="thumbs_up"
label={intl.formatMessage(intlMessages.thumbsupLabel)} label={intl.formatMessage(intlMessages.thumbsupLabel)}
description={intl.formatMessage(intlMessages.thumbsupDesc)} description={intl.formatMessage(intlMessages.thumbsupDesc)}
onClick={() => actions.setEmojiHandler('thumbsUp')} onClick={() => actions.setEmojiHandler('thumbsUp')}
tabIndex={-1}
/> />
<DropdownListItem <DropdownListItem
icon="thumbs_down" icon="thumbs_down"
label={intl.formatMessage(intlMessages.thumbsdownLabel)} label={intl.formatMessage(intlMessages.thumbsdownLabel)}
description={intl.formatMessage(intlMessages.thumbsdownDesc)} description={intl.formatMessage(intlMessages.thumbsdownDesc)}
onClick={() => actions.setEmojiHandler('thumbsDown')} onClick={() => actions.setEmojiHandler('thumbsDown')}
tabIndex={-1}
/> />
<DropdownListItem <DropdownListItem
icon="applause" icon="applause"
label={intl.formatMessage(intlMessages.applauseLabel)} label={intl.formatMessage(intlMessages.applauseLabel)}
description={intl.formatMessage(intlMessages.applauseDesc)} description={intl.formatMessage(intlMessages.applauseDesc)}
onClick={() => actions.setEmojiHandler('applause')} onClick={() => actions.setEmojiHandler('applause')}
tabIndex={-1}
/> />
<DropdownListSeparator /> <DropdownListSeparator />
<DropdownListItem <DropdownListItem
@ -112,6 +208,7 @@ class EmojiMenu extends Component {
label={intl.formatMessage(intlMessages.clearLabel)} label={intl.formatMessage(intlMessages.clearLabel)}
description={intl.formatMessage(intlMessages.clearDesc)} description={intl.formatMessage(intlMessages.clearDesc)}
onClick={() => actions.setEmojiHandler('none')} onClick={() => actions.setEmojiHandler('none')}
tabIndex={-1}
/> />
</DropdownList> </DropdownList>
</DropdownContent> </DropdownContent>

View File

@ -60,30 +60,30 @@ export default class DropdownList extends Component {
return event.currentTarget.click(); return event.currentTarget.click();
} }
//let nextActiveItemIndex = null; let nextActiveItemIndex = null;
//if (KEY_CODES.ARROW_UP === event.which) { if (KEY_CODES.ARROW_UP === event.which) {
// nextActiveItemIndex = activeItemIndex - 1; nextActiveItemIndex = activeItemIndex - 1;
//} }
//if (KEY_CODES.ARROW_DOWN === event.which) { if (KEY_CODES.ARROW_DOWN === event.which) {
// nextActiveItemIndex = activeItemIndex + 1; nextActiveItemIndex = activeItemIndex + 1;
//} }
//if (nextActiveItemIndex > (this.childrenRefs.length - 1)) { if (nextActiveItemIndex > (this.childrenRefs.length - 1)) {
// nextActiveItemIndex = 0; nextActiveItemIndex = 0;
//} }
//if (nextActiveItemIndex < 0) { if (nextActiveItemIndex < 0) {
// nextActiveItemIndex = this.childrenRefs.length - 1; nextActiveItemIndex = this.childrenRefs.length - 1;
//} }
//if ([KEY_CODES.TAB, KEY_CODES.ESCAPE].includes(event.which)) { if ([KEY_CODES.ESCAPE].includes(event.which)) {
// nextActiveItemIndex = 0; nextActiveItemIndex = 0;
// dropdownHide(); dropdownHide();
//} }
//this.setState({ activeItemIndex: nextActiveItemIndex }); this.setState({ activeItemIndex: nextActiveItemIndex });
if (typeof callback === 'function') { if (typeof callback === 'function') {
callback(event); callback(event);

View File

@ -29,14 +29,16 @@ export default class DropdownListItem extends Component {
render() { render() {
const { label, description, children, injectRef, tabIndex, onClick, onKeyDown, const { label, description, children, injectRef, tabIndex, onClick, onKeyDown,
className, style, separator, intl, } = this.props; className, style, separator, intl, placeInTabOrder } = this.props;
let index = (placeInTabOrder) ? 0 : -1;
return ( return (
<li <li
ref={injectRef} ref={injectRef}
onClick={onClick} onClick={onClick}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
tabIndex={tabIndex} tabIndex={index}
aria-labelledby={this.labelID} aria-labelledby={this.labelID}
aria-describedby={this.descID} aria-describedby={this.descID}
className={cx(styles.item, className)} className={cx(styles.item, className)}

View File

@ -41,14 +41,16 @@ export default class DropdownTrigger extends Component {
} }
render() { render() {
const { children, style, className, } = this.props; const { children, style, className, placeInTabOrder} = this.props;
const TriggerComponent = React.Children.only(children); const TriggerComponent = React.Children.only(children);
let index = (placeInTabOrder) ? '0' : '-1';
const TriggerComponentBounded = React.cloneElement(children, { const TriggerComponentBounded = React.cloneElement(children, {
onClick: this.handleClick, onClick: this.handleClick,
onKeyDown: this.handleKeyDown, onKeyDown: this.handleKeyDown,
'aria-haspopup': true, 'aria-haspopup': true,
tabIndex: '-1', tabIndex: index,
style: style, style: style,
className: cx(children.props.className, className), className: cx(children.props.className, className),
}); });

View File

@ -84,7 +84,7 @@ class SettingsDropdown extends Component {
return ( return (
<Dropdown ref="dropdown" autoFocus={true}> <Dropdown ref="dropdown" autoFocus={true}>
<DropdownTrigger> <DropdownTrigger placeInTabOrder={true}>
<Button <Button
label={intl.formatMessage(intlMessages.optionsLabel)} label={intl.formatMessage(intlMessages.optionsLabel)}
icon="more" icon="more"

View File

@ -61,7 +61,8 @@ class UserList extends Component {
count = openChats.length; count = openChats.length;
} }
if(this.counter === -1){ if(this.counter === -1 || this.counter > count){
active.tabIndex = -1;
list.tabIndex = 0; list.tabIndex = 0;
this.counter = 0; this.counter = 0;
list.focus(); list.focus();

View File

@ -238,7 +238,7 @@ class UserListItem extends Component {
let actions = this.getAvailableActions(); let actions = this.getAvailableActions();
let contents = ( let contents = (
<div className={cx(styles.userListItem, userItemContentsStyle)}> <div className={cx(styles.userListItem, userItemContentsStyle)}>
<div className={styles.userItemContents} > <div className={styles.userItemContents}>
<UserAvatar user={user} /> <UserAvatar user={user} />
{this.renderUserName()} {this.renderUserName()}
{this.renderUserIcons()} {this.renderUserIcons()}
@ -444,6 +444,7 @@ class UserListItem extends Component {
defaultMessage={action.label} defaultMessage={action.label}
onClick={action.handler.bind(this, ...parameters)} onClick={action.handler.bind(this, ...parameters)}
ref={"test"} ref={"test"}
placeInTabOrder={true}
/> />
); );