-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Focus Zone: Allow Tab to Skip Selection #4061
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
Conversation
| private _isInnerZone: boolean; | ||
|
|
||
| /** Used to allow us to move to next focusable element even when we're focusing on a input element when pressing tab */ | ||
| private _tabPressActive: boolean; |
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.
maybe a better name is _processingTabKey or something like that since this flag is for when the tab key press is being processed
| } | ||
| this._tabPressActive = false; | ||
| if (focusChanged) { | ||
| break; |
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.
Why is the code now only breaking if focusChanged?
|
|
||
| if (this._isElementInput(element)) { | ||
| if (!this._shouldInputLoseFocus(element as HTMLInputElement, isForward)) { | ||
| if (!this._tabPressActive && !this._shouldInputLoseFocus(element as HTMLInputElement, isForward)) { |
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.
Consider moving this check into shouldInputLoseFocus since it's directly related to if the input should lose focus when tabbing
jspurlin
left a comment
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.
A few minor changes are needed but looks good overall
|
Pull the latest changes from master, this PR will need to be updated with the changes made in #3962 |
| * and have to hit escape or some other key. | ||
| */ | ||
| allowTabKey?: boolean; | ||
| tabPermission?: FocusZoneTabbableElements; |
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.
Let's go ahead and deprecate allowTabKey instead of removing it. Remove all references to it (setting the value to tabPermission if it is passed in), put at @deprecated in the jsdoc comment, and add a _warnDeprecations for that prop referencing tabPermission
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.
Consider updating the prop name, maybe handleTabKey
| checkForNoWrap?: boolean; | ||
| } | ||
|
|
||
| export enum FocusZoneTabbableElements { |
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.
export const enum ... it will help keep size down.
| } | ||
|
|
||
| private _shouldInputLoseFocus(element: HTMLInputElement, isForward?: boolean) { | ||
| if (this.props.tabPermission === FocusZoneTabbableElements.inputOnly && !this._processingTabKey) { |
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.
You don't know that the element is an input at this point, this check should be moved down to the if statement below.
|
|
||
| private _shouldInputLoseFocus(element: HTMLInputElement, isForward?: boolean) { | ||
| if (element && | ||
| if (this.props.tabPermission === FocusZoneTabbableElements.inputOnly && !this._processingTabKey) { |
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.
You don't know if the element is an input element at this point. This needs to be moved down to the if statement below.
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.
I believe we should. The element we pass in is of type: HTMLInputElement
However, I think it would make more sense to add it with the rest of the cases, so I'll move it down.
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.
Inputs can be of different types, that's why the check below is set up the way that it is
|
|
||
| // allowTabKeyOnInput is true and allowTabKey is false | ||
| inputOnly = 2 | ||
| } |
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.
If you update the prop name to something like handleTabKey the enum name and string values should be updated... for the values maybe never = 0, always= 1, inputOnly = 2
| const component = ReactTestUtils.renderIntoDocument( | ||
| <div { ...{ onFocusCapture: _onFocus, onKeyDown: tabDownListener } }> | ||
| <FocusZone {...{ allowTabKey: true, isCircularNavigation: true }}> | ||
| <FocusZone {...{ tabPermission: FocusZoneTabbableElements.all, isCircularNavigation: true }}> |
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.
You should add a new test case for inputOnly
| case KeyCodes.tab: | ||
| if (this.props.allowTabKey) { | ||
| if (this.props.tabPermission === FocusZoneTabbableElements.all | ||
| || (this.props.tabPermission === FocusZoneTabbableElements.inputOnly && this._isElementInput(ev.target as HTMLElement))) { |
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.
put the || at the end of the previous line, it makes the if statement more readable
jspurlin
left a comment
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.
some minor updates need to be made
| <FocusZone> | ||
| <button className='a'>a</button> | ||
| </FocusZone> | ||
| </FocusZone>g |
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.
Did you mean to have this 'g' there?
jspurlin
left a comment
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.
Looks good
| if (this.props.allowTabKey) { | ||
| if (this.props.allowTabKey || | ||
| this.props.handleTabKey === FocusZoneTabbableElements.all || | ||
| (this.props.handleTabKey === FocusZoneTabbableElements.inputOnly && this._isElementInput(ev.target as HTMLElement))) { |
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.
Put this onto multiple lines
* master: (28 commits) Scrollable pane sort stickies (microsoft#4111) Allow ScrollablePane to accept native properties. (microsoft#4095) Sticky (microsoft#4091) Applying package updates. [ColorRectangle, Sticky] Fixed null root refs (microsoft#4099) DatePicker: order of callbacks for onSelectDate and onAfterMenuDismiss (microsoft#4092) Applying package updates. Alhenry fix split button props (microsoft#4090) Fixing ComboBox styling by reverting button classname move (microsoft#4088) Update CODEOWNERS Undoing terrible change. [DetailsList] Fixed focus test (microsoft#4087) Added icons package screener test (microsoft#4082) ContextualMenu: Fix ContextualMenuUtility imports (microsoft#4085) [ContextualMenu] Made disabled buttons focusable (microsoft#4074) Convert Check to mergeStyles (microsoft#3880) [DetailsList] Add public focusIndex function (microsoft#3852) ComboBox button should have data-is-focusable="false" (microsoft#4070) Applying package updates. Focus Zone: Allow Tab to Skip Selection (microsoft#4061) ...
Problem:
If tabbing is enabled, we have a problem where we can't tab through input html elements and we would be stuck on the input button
Solution:
The reason for this problem is, because when we're trying to switch focus on an input element, we would stop the focus. For arrow keys that would be the correct thing to do, however if we're tabbing, we want to ignore that and focus on the next element.
I also changed the way we handle tab to be more like arrow keys where we only stop if we change focus otherwise we'll let the tab event to continue to propagate.
Validation:
I added a new example component that allows tabbing and circular navigation along with an text input so that we can more easily test to see if have a regression in the focus zone where we would get stuck an an input element.
Here is what it is now:

Here is what it was before where it get stuck:
