-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Removing ability to focus with keyboarding on menu buttons for split buttons and and spin boxes #4222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Removing ability to focus with keyboarding on menu buttons for split buttons and and spin boxes #4222
Changes from all commits
2ffef62
979da51
e4ab4b4
3d95aca
64bc57b
e08bf24
c16992b
7bea2c0
62cb458
efb7439
f803332
b2a4475
b1f2c39
976c67f
d3e1426
0aaefaa
bbc370f
0fc2cc6
965a8e9
2603277
aaa77c3
ad6e5df
e01436a
a33679e
93ba4ae
d19e1a6
24ae278
0e7d887
619711d
276fe96
d4436d4
843d758
9186034
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "office-ui-fabric-react", | ||
| "comment": "Removed focusability on buttons for split buttons and spin buttons", | ||
| "type": "patch" | ||
| } | ||
| ], | ||
| "packageName": "office-ui-fabric-react", | ||
| "email": "[email protected]" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -163,7 +163,7 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| 'aria-label': ariaLabel, | ||
| 'aria-labelledby': ariaLabelledBy, | ||
| 'aria-describedby': ariaDescribedBy, | ||
| 'data-is-focusable': ((this.props as any)['data-is-focusable'] === false || disabled) ? false : true, | ||
| 'data-is-focusable': ((this.props as any)['data-is-focusable'] === false || disabled || this._isSplitButton) ? false : true, | ||
| 'aria-pressed': checked | ||
| } | ||
| ); | ||
|
|
@@ -200,7 +200,9 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| } | ||
|
|
||
| public focus(): void { | ||
| if (this._buttonElement.value) { | ||
| if (this._isSplitButton && this._splitButtonContainer.value) { | ||
| this._splitButtonContainer.value.focus(); | ||
| } else if (this._buttonElement.value) { | ||
| this._buttonElement.value.focus(); | ||
| } | ||
| } | ||
|
|
@@ -409,6 +411,10 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| } | ||
|
|
||
| private _onToggleMenu = (): void => { | ||
| if (this._splitButtonContainer.value) { | ||
| this._splitButtonContainer.value.focus(); | ||
| } | ||
| const { menuProps } = this.props; | ||
| const currentMenuProps = this.state.menuProps; | ||
| currentMenuProps ? this._dismissMenu() : this._openMenu(); | ||
| } | ||
|
|
@@ -432,7 +438,14 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| !!this.state.menuProps, | ||
| !!checked); | ||
|
|
||
| buttonProps = { ...buttonProps, onClick: undefined }; | ||
| assign( | ||
| buttonProps, | ||
| { | ||
| onClick: undefined, | ||
| tabIndex: -1, | ||
| 'data-is-focusable': false | ||
| } | ||
| ); | ||
|
|
||
| return ( | ||
| <div | ||
|
|
@@ -444,10 +457,11 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| aria-pressed={ this.props.checked } | ||
| aria-describedby={ buttonProps.ariaDescription } | ||
| className={ classNames && classNames.splitButtonContainer } | ||
| onKeyDown={ this._onMenuKeyDown } | ||
| onKeyDown={ this._onSplitButtonContainerKeyDown } | ||
| ref={ this._splitButtonContainer } | ||
| data-is-focusable={ true } | ||
| onClick={ !disabled && !primaryDisabled ? onClick : undefined } | ||
| tabIndex={ !disabled ? 0 : undefined } | ||
| > | ||
| <span | ||
| style={ { 'display': 'flex' } } | ||
|
|
@@ -491,11 +505,10 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| 'iconProps': menuIconProps, | ||
| 'ariaLabel': splitButtonAriaLabel, | ||
| 'aria-haspopup': true, | ||
| 'aria-expanded': this._isExpanded | ||
| 'aria-expanded': this._isExpanded, | ||
| 'data-is-focusable': false | ||
| }; | ||
|
|
||
| return <BaseButton { ...splitButtonProps } onMouseDown={ this._onMouseDown } />; | ||
|
|
||
| return <BaseButton {...splitButtonProps} onMouseDown={ this._onMouseDown } tabIndex={ -1 } />; | ||
| } | ||
|
|
||
| private _onMouseDown = (ev: React.MouseEvent<BaseButton>) => { | ||
|
|
@@ -506,7 +519,23 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| ev.preventDefault(); | ||
| } | ||
|
|
||
| private _onSplitButtonContainerKeyDown = (ev: React.KeyboardEvent<HTMLDivElement>) => { | ||
| if (ev.which === KeyCodes.enter) { | ||
| if (this._buttonElement.value) { | ||
| this._buttonElement.value.click(); | ||
| ev.preventDefault(); | ||
| ev.stopPropagation(); | ||
| } | ||
| } else { | ||
| this._onMenuKeyDown(ev); | ||
| } | ||
| } | ||
|
|
||
| private _onMenuKeyDown = (ev: React.KeyboardEvent<HTMLDivElement | HTMLAnchorElement | HTMLButtonElement>) => { | ||
| if (this.props.disabled) { | ||
| return; | ||
| } | ||
|
|
||
| if (this.props.onKeyDown) { | ||
| this.props.onKeyDown(ev); | ||
| } | ||
|
|
@@ -518,13 +547,26 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems strange that this calls onMenuClick (considering that this is keydown)... If this want to call onMenuClick, why isn't this calling _onMenuClick?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We want to trigger the primary action when we hit the enter button if we're focusing on the html element. This works for buttons, unfortunately, the container is a div so pressing enter won't trigger the onClick event. I can move the code in onMenuClick and call it in both places, but they would both also reference onClick events. What do you think?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think as long as _onToggleMenu is updating the focus correctly this is fine
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is resolved, we'll keep this code here and add the focus change to onToggleMenu |
||
| if (!ev.defaultPrevented && | ||
| this.props.menuTriggerKeyCode !== null && | ||
| ev.which === (this.props.menuTriggerKeyCode === undefined ? KeyCodes.down : this.props.menuTriggerKeyCode)) { | ||
| this._isValidMenuOpenKey(ev)) { | ||
| this._onToggleMenu(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as part of _onToggleMenu could we set focus to the container before setting the state to toggle the menu? This would allow focus to return to the whole split button once the menu is dismissed
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would that be what we want to do? By doing this we would force the focus to go back to the menu in every scenario. From my understanding, we want focus to go back to the previous focused element, wIth the suggested change, we always focus on the button when we close the menu.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The focus should always only be on the container (even if it was fired against the split button portion). We should never be setting focus on the individual portions of the splitButton
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the changes here. Now we only don't focus the split button if everything is disabled. |
||
| ev.preventDefault(); | ||
| ev.stopPropagation(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns if the user hits a valid keyboard key to open the menu | ||
| * @param ev - the keyboard event | ||
| * @returns True if user clicks on custom trigger key if enabled or alt + down arrow if not. False otherwise. | ||
| */ | ||
| private _isValidMenuOpenKey(ev: React.KeyboardEvent<HTMLDivElement | HTMLAnchorElement | HTMLButtonElement>): boolean { | ||
| if (this.props.menuTriggerKeyCode) { | ||
| return ev.which === this.props.menuTriggerKeyCode; | ||
| } else { | ||
| return ev.which === KeyCodes.down && (ev.altKey || ev.metaKey); | ||
| } | ||
| } | ||
|
|
||
| private _onMenuClick = (ev: React.MouseEvent<HTMLAnchorElement>) => { | ||
| const { onMenuClick } = this.props; | ||
| if (onMenuClick) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tabindex -1 still seems like a bad idea