Skip to content
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

Focus Zone: Allow Tab to Skip Selection #4061

Merged
merged 19 commits into from
Feb 23, 2018

Conversation

chang47
Copy link
Contributor

@chang47 chang47 commented Feb 21, 2018

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:
focus zone don t get stuck

Here is what it was before where it get stuck:
focus zone get stuck

@@ -58,6 +58,9 @@ export class FocusZone extends BaseComponent<IFocusZoneProps, {}> implements IFo
private _focusAlignment: IPoint;
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;
Copy link
Collaborator

@jspurlin jspurlin Feb 21, 2018

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

focusChanged = ev.shiftKey ? this._moveFocusLeft() : this._moveFocusRight();
}
this._tabPressActive = false;
if (focusChanged) {
break;
Copy link
Collaborator

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?

@@ -487,7 +488,7 @@ export class FocusZone extends BaseComponent<IFocusZoneProps, {}> implements IFo
}

if (this._isElementInput(element)) {
if (!this._shouldInputLoseFocus(element as HTMLInputElement, isForward)) {
if (!this._tabPressActive && !this._shouldInputLoseFocus(element as HTMLInputElement, isForward)) {
Copy link
Collaborator

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

Copy link
Collaborator

@jspurlin jspurlin left a 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

@jspurlin
Copy link
Collaborator

Pull the latest changes from master, this PR will need to be updated with the changes made in #3962

@@ -109,7 +109,7 @@ export interface IFocusZoneProps extends React.HTMLAttributes<HTMLElement | Focu
* an unfortunate side effect is that users will not be able to tab out of the focus zone
* and have to hit escape or some other key.
*/
allowTabKey?: boolean;
tabPermission?: FocusZoneTabbableElements;
Copy link
Collaborator

@jspurlin jspurlin Feb 22, 2018

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

Copy link
Collaborator

@jspurlin jspurlin Feb 22, 2018

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

@@ -119,6 +119,18 @@ export interface IFocusZoneProps extends React.HTMLAttributes<HTMLElement | Focu
checkForNoWrap?: boolean;
}

export enum FocusZoneTabbableElements {
Copy link
Contributor

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.

@@ -788,6 +790,10 @@ export class FocusZone extends BaseComponent<IFocusZoneProps, {}> implements IFo
}

private _shouldInputLoseFocus(element: HTMLInputElement, isForward?: boolean) {
if (this.props.tabPermission === FocusZoneTabbableElements.inputOnly && !this._processingTabKey) {
Copy link
Contributor

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.

@@ -787,7 +790,13 @@ export class FocusZone extends BaseComponent<IFocusZoneProps, {}> implements IFo
}

private _shouldInputLoseFocus(element: HTMLInputElement, isForward?: boolean) {
if (element &&
if (this.props.tabPermission === FocusZoneTabbableElements.inputOnly && !this._processingTabKey) {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Collaborator

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
}
Copy link
Collaborator

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

@@ -1048,7 +1048,7 @@ describe('FocusZone', () => {
const tabDownListener = jest.fn();
const component = ReactTestUtils.renderIntoDocument(
<div { ...{ onFocusCapture: _onFocus, onKeyDown: tabDownListener } }>
<FocusZone {...{ allowTabKey: true, isCircularNavigation: true }}>
<FocusZone {...{ tabPermission: FocusZoneTabbableElements.all, isCircularNavigation: true }}>
Copy link
Collaborator

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

@@ -361,21 +367,18 @@ export class FocusZone extends BaseComponent<IFocusZoneProps, {}> implements IFo
return;

case KeyCodes.tab:
if (this.props.allowTabKey) {
if (this.props.tabPermission === FocusZoneTabbableElements.all
|| (this.props.tabPermission === FocusZoneTabbableElements.inputOnly && this._isElementInput(ev.target as HTMLElement))) {
Copy link
Collaborator

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

Copy link
Collaborator

@jspurlin jspurlin left a 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

@@ -1132,7 +1132,7 @@ describe('FocusZone', () => {
<div { ...{ onFocusCapture: _onFocus, onKeyDown: tabDownListener } }>
<FocusZone>
<button className='a'>a</button>
</FocusZone>
</FocusZone>g
Copy link
Collaborator

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?

Copy link
Collaborator

@jspurlin jspurlin left a 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))) {
Copy link
Contributor

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

@jspurlin jspurlin merged commit 1de56b1 into microsoft:master Feb 23, 2018
Markionium added a commit to Markionium/office-ui-fabric-react that referenced this pull request Feb 27, 2018
* 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)
  ...
chrismohr pushed a commit to chrismohr/office-ui-fabric-react that referenced this pull request Apr 17, 2018
* added return for focus zone when focus change fail

* add tab override for focus zone

* added support for skipping selection with tab

* add change files

* added tabbable focus zone example

* got rid of unnecessary return

* used member instead of passing varaables down

* changed tab field name; moved tab location

* removed duplicate component; fixed lint problems

* added actual tabbable example scss to fix build crash

* Adding FocusZoneTabbableElements enum

* add new input only mode that allows skipping of focus on tab for input elements

* cleaned things up and added better comments

* updated pr for everything except test

* added unit test

* optimized if statement; cleaned up test

* separated ampersand for if statement

* update all lint probelms
@microsoft microsoft locked as resolved and limited conversation to collaborators Aug 31, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants