From 6ea9aced6a0495d50b5633a3c1e2d3b1adf8e128 Mon Sep 17 00:00:00 2001 From: David Zearing Date: Fri, 22 Mar 2019 10:53:38 -0700 Subject: [PATCH 1/5] Fix for focus alignment. --- .../src/components/FocusZone/FocusZone.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx b/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx index 2d3e703af8d23c..a9ecd12696d937 100644 --- a/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx @@ -309,11 +309,11 @@ export class FocusZone extends React.Component implements I } if (newActiveElement && newActiveElement !== this._activeElement) { - this._activeElement = newActiveElement; - - if (isImmediateDescendant) { - this._setFocusAlignment(this._activeElement); + if (isImmediateDescendant || !this._activeElement) { + this._setFocusAlignment(newActiveElement, true, true); } + + this._activeElement = newActiveElement; } if (onActiveElementChanged) { From d9bd53b48d90faca3e3a26c6325c6a8a2cd49c99 Mon Sep 17 00:00:00 2001 From: David Zearing Date: Fri, 22 Mar 2019 10:55:20 -0700 Subject: [PATCH 2/5] Add change file. --- .../focuszone-align_2019-03-22-17-55.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/office-ui-fabric-react/focuszone-align_2019-03-22-17-55.json diff --git a/common/changes/office-ui-fabric-react/focuszone-align_2019-03-22-17-55.json b/common/changes/office-ui-fabric-react/focuszone-align_2019-03-22-17-55.json new file mode 100644 index 00000000000000..63f9f057d1977f --- /dev/null +++ b/common/changes/office-ui-fabric-react/focuszone-align_2019-03-22-17-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "office-ui-fabric-react", + "comment": "FocusZone: focus alignment is now reset when first receiving focus (programatically or via `defaultActiveElement`.", + "type": "patch" + } + ], + "packageName": "office-ui-fabric-react", + "email": "dzearing@microsoft.com" +} \ No newline at end of file From 33ecc18ffab1396ae50a3f787868db008876ce1a Mon Sep 17 00:00:00 2001 From: David Zearing Date: Fri, 22 Mar 2019 10:59:16 -0700 Subject: [PATCH 3/5] Example cleanup. --- .../examples/FocusZone.Photos.Example.tsx | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/FocusZone/examples/FocusZone.Photos.Example.tsx b/packages/office-ui-fabric-react/src/components/FocusZone/examples/FocusZone.Photos.Example.tsx index a210307742ddd0..fc61de1f8180bf 100644 --- a/packages/office-ui-fabric-react/src/components/FocusZone/examples/FocusZone.Photos.Example.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusZone/examples/FocusZone.Photos.Example.tsx @@ -11,7 +11,6 @@ export interface IPhoto { url: string; width: number; height: number; - onClick: (ev: React.MouseEvent) => void; } export class FocusZonePhotosExample extends React.Component<{}, { items: IPhoto[] }> { @@ -27,7 +26,6 @@ export class FocusZonePhotosExample extends React.Component<{}, { items: IPhoto[ return ( -

Note: clicking on the items below will remove them, illustrating how FocusZone will retain focus even when items are removed.

{items.map((item: IPhoto, index) => (
  • @@ -63,20 +60,7 @@ export class FocusZonePhotosExample extends React.Component<{}, { items: IPhoto[ id, url: `http://placehold.it/${randomWidth}x100`, width: randomWidth, - height: 100, - onClick: (ev: React.MouseEvent) => { - const items: IPhoto[] = this.state.items.filter((item: IPhoto) => item.id !== id); - - this.setState({ items }); - - // If we have run out of items, repopulate in a couple seconds. - if (items.length === 0) { - setTimeout(() => this.setState({ items: this._getInitialItems() }), 2000); - } - - ev.preventDefault(); - ev.stopPropagation(); - } + height: 100 }; } } From 1d90ae5991f034bbbb6eef62cc25be24420bd6bd Mon Sep 17 00:00:00 2001 From: David Zearing Date: Fri, 22 Mar 2019 11:07:23 -0700 Subject: [PATCH 4/5] Adding test coverage. --- .../components/FocusZone/FocusZone.test.tsx | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.test.tsx b/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.test.tsx index 9b6041b04053ec..1505a9aef2df05 100644 --- a/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.test.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.test.tsx @@ -704,6 +704,75 @@ describe('FocusZone', () => { setRTL(false); }); + it('can focus correctly when receiving initial focus, bidirectionally', () => { + const component = ReactTestUtils.renderIntoDocument( +
    + + + + + + +
    + ); + + const focusZone = ReactDOM.findDOMNode(component as React.ReactInstance)!.firstChild as Element; + const buttonA = focusZone.querySelector('.a') as HTMLElement; + const buttonB = focusZone.querySelector('.b') as HTMLElement; + const buttonC = focusZone.querySelector('.c') as HTMLElement; + const buttonD = focusZone.querySelector('.d') as HTMLElement; + + // Set up a grid like so: + // A B + // C D + // + // We will focus B, and then down arrow, expecting D to be focused. + + setupElement(buttonA, { + clientRect: { + top: 0, + bottom: 20, + left: 0, + right: 30 + } + }); + + setupElement(buttonB, { + clientRect: { + top: 0, + bottom: 20, + left: 20, + right: 40 + } + }); + + setupElement(buttonC, { + clientRect: { + top: 20, + bottom: 40, + left: 0, + right: 20 + } + }); + + setupElement(buttonD, { + clientRect: { + top: 20, + bottom: 40, + left: 20, + right: 40 + } + }); + + // Focus the first button. + ReactTestUtils.Simulate.focus(buttonB); + expect(lastFocusedElement).toBe(buttonB); + + // Pressing down should go to d. + ReactTestUtils.Simulate.keyDown(focusZone, { which: KeyCodes.down }); + expect(lastFocusedElement).toBe(buttonD); + }); + it('can use arrows bidirectionally with data-no-vertical-wrap', () => { const component = ReactTestUtils.renderIntoDocument(
    From 18e365cbaa3b06e2ebac6484178760e2f42109be Mon Sep 17 00:00:00 2001 From: David Zearing Date: Fri, 22 Mar 2019 15:15:20 -0700 Subject: [PATCH 5/5] Updating tab indexes on initial focus --- .../components/FocusZone/FocusZone.test.tsx | 2 ++ .../src/components/FocusZone/FocusZone.tsx | 12 ++++++++-- .../FocusZone.Photos.Example.tsx.shot | 23 ------------------- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.test.tsx b/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.test.tsx index 1505a9aef2df05..38dbbca03d3b5d 100644 --- a/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.test.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.test.tsx @@ -766,6 +766,8 @@ describe('FocusZone', () => { // Focus the first button. ReactTestUtils.Simulate.focus(buttonB); + expect(buttonA.getAttribute('tabindex')).toBe('-1'); + expect(buttonB.getAttribute('tabindex')).toBe('0'); expect(lastFocusedElement).toBe(buttonB); // Pressing down should go to d. diff --git a/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx b/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx index a9ecd12696d937..06b46f229cd708 100644 --- a/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx @@ -308,12 +308,20 @@ export class FocusZone extends React.Component implements I } } + const initialElementFocused = !this._activeElement; + + // If the new active element is a child of this zone and received focus, + // update alignment an immediate descendant if (newActiveElement && newActiveElement !== this._activeElement) { - if (isImmediateDescendant || !this._activeElement) { - this._setFocusAlignment(newActiveElement, true, true); + if (isImmediateDescendant || initialElementFocused) { + this._setFocusAlignment(newActiveElement, initialElementFocused); } this._activeElement = newActiveElement; + + if (initialElementFocused) { + this._updateTabIndexes(); + } } if (onActiveElementChanged) { diff --git a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusZone.Photos.Example.tsx.shot b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusZone.Photos.Example.tsx.shot index 0af554c1597579..9acebf80fa5566 100644 --- a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusZone.Photos.Example.tsx.shot +++ b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusZone.Photos.Example.tsx.shot @@ -9,16 +9,12 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] = onMouseDownCapture={[Function]} role="presentation" > -

    - Note: clicking on the items below will remove them, illustrating how FocusZone will retain focus even when items are removed. -