diff --git a/common/changes/office-ui-fabric-react/jolore-focusZoneUpdate_2018-04-07-00-43.json b/common/changes/office-ui-fabric-react/jolore-focusZoneUpdate_2018-04-07-00-43.json new file mode 100644 index 00000000000000..0fc71f8d07094b --- /dev/null +++ b/common/changes/office-ui-fabric-react/jolore-focusZoneUpdate_2018-04-07-00-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "office-ui-fabric-react", + "comment": "FocusZone: updating to update alignment on click.", + "type": "patch" + } + ], + "packageName": "office-ui-fabric-react", + "email": "jolore@microsoft.com" +} diff --git a/packages/experiments/src/components/CommandBar/__snapshots__/CommandBar.test.tsx.snap b/packages/experiments/src/components/CommandBar/__snapshots__/CommandBar.test.tsx.snap index 7c7eab154bbbcb..d9fa592f5519fa 100644 --- a/packages/experiments/src/components/CommandBar/__snapshots__/CommandBar.test.tsx.snap +++ b/packages/experiments/src/components/CommandBar/__snapshots__/CommandBar.test.tsx.snap @@ -44,6 +44,7 @@ exports[`CommandBar renders commands correctly 1`] = ` flex-grow: 1; } data-focuszone-id="FocusZone0" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} @@ -286,6 +287,7 @@ exports[`CommandBar renders commands correctly 1`] = ` flex-shrink: 0; } data-focuszone-id="FocusZone7" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/Breadcrumb/__snapshots__/Breadcrumb.test.tsx.snap b/packages/office-ui-fabric-react/src/components/Breadcrumb/__snapshots__/Breadcrumb.test.tsx.snap index 288b828f23dcfc..d262d479dbead1 100644 --- a/packages/office-ui-fabric-react/src/components/Breadcrumb/__snapshots__/Breadcrumb.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/Breadcrumb/__snapshots__/Breadcrumb.test.tsx.snap @@ -27,6 +27,7 @@ exports[`Breadcrumb renders breadcumb correctly 1`] = ` aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone0" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} @@ -171,6 +172,7 @@ exports[`Breadcrumb renders breadcumb correctly 2`] = ` aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone5" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} @@ -380,6 +382,7 @@ exports[`Breadcrumb renders breadcumb correctly 3`] = ` aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone11" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/Calendar/__snapshots__/Calendar.test.tsx.snap b/packages/office-ui-fabric-react/src/components/Calendar/__snapshots__/Calendar.test.tsx.snap index 507065a96d6f5d..da925f1c914748 100644 --- a/packages/office-ui-fabric-react/src/components/Calendar/__snapshots__/Calendar.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/Calendar/__snapshots__/Calendar.test.tsx.snap @@ -91,6 +91,7 @@ exports[`Calendar Test rendering simplest calendar Renders simple calendar corre aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone12" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} @@ -958,6 +959,7 @@ exports[`Calendar Test rendering simplest calendar Renders simple calendar corre aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone13" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/CommandBar/__snapshots__/CommandBar.test.tsx.snap b/packages/office-ui-fabric-react/src/components/CommandBar/__snapshots__/CommandBar.test.tsx.snap index cdfa05f114431b..f4d10a32c15ac3 100644 --- a/packages/office-ui-fabric-react/src/components/CommandBar/__snapshots__/CommandBar.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/CommandBar/__snapshots__/CommandBar.test.tsx.snap @@ -9,6 +9,7 @@ exports[`CommandBar renders CommandBar correctly 1`] = ` aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone1" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/DetailsList/__snapshots__/DetailsHeader.test.tsx.snap b/packages/office-ui-fabric-react/src/components/DetailsList/__snapshots__/DetailsHeader.test.tsx.snap index 311fa3eb38b373..6240400730b830 100644 --- a/packages/office-ui-fabric-react/src/components/DetailsList/__snapshots__/DetailsHeader.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/DetailsList/__snapshots__/DetailsHeader.test.tsx.snap @@ -8,6 +8,7 @@ exports[`DetailsHeader can render 1`] = ` className="ms-FocusZone ms-DetailsHeader" data-automationid="DetailsHeader" data-focuszone-id="FocusZone1" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/DetailsList/__snapshots__/DetailsList.test.tsx.snap b/packages/office-ui-fabric-react/src/components/DetailsList/__snapshots__/DetailsList.test.tsx.snap index d4d66656bcecad..fbdb6a331f19d2 100644 --- a/packages/office-ui-fabric-react/src/components/DetailsList/__snapshots__/DetailsList.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/DetailsList/__snapshots__/DetailsList.test.tsx.snap @@ -34,6 +34,7 @@ exports[`DetailsList renders List correctly 1`] = ` className="ms-FocusZone ms-DetailsHeader" data-automationid="DetailsHeader" data-focuszone-id="FocusZone1" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} @@ -203,6 +204,7 @@ exports[`DetailsList renders List correctly 1`] = ` className="ms-FocusZone" data-focuszone-id="FocusZone2" onBlur={[Function]} + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/Facepile/__snapshots__/Facepile.test.tsx.snap b/packages/office-ui-fabric-react/src/components/Facepile/__snapshots__/Facepile.test.tsx.snap index a75223fb0d25fa..62c741bd429e4a 100644 --- a/packages/office-ui-fabric-react/src/components/Facepile/__snapshots__/Facepile.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/Facepile/__snapshots__/Facepile.test.tsx.snap @@ -12,6 +12,7 @@ exports[`Facepile renders Facepile correctly 1`] = ` aria-labelledby={undefined} className="ms-FocusZone ms-Facepile-members" data-focuszone-id="FocusZone1" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} 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 d5972db64ea2c6..21ee8495b7b589 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 @@ -729,6 +729,87 @@ describe('FocusZone', () => { expect(lastFocusedElement).toBe(buttonA); }); + it('click resets focus alignment when bidirectional', () => { + 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 + setupElement(buttonA, { + clientRect: { + top: 0, + bottom: 20, + left: 0, + right: 20 + } + }); + + 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(buttonA); + expect(lastFocusedElement).toBe(buttonA); + + // Clicking on b should focus b, and reset the focus alignment to the second column + // note that a click in a browser fires mouseDown, focus, then click events + ReactTestUtils.Simulate.focus(buttonB); + ReactTestUtils.Simulate.click(buttonB); + expect(lastFocusedElement).toBe(buttonB); + + // Pressing down should go to d. + ReactTestUtils.Simulate.keyDown(focusZone, { which: KeyCodes.down }); + expect(lastFocusedElement).toBe(buttonD); + + // Clicking on c should focus c, and reset the focus alignment to the first column + ReactTestUtils.Simulate.focus(buttonC); + ReactTestUtils.Simulate.click(buttonC); + expect(lastFocusedElement).toBe(buttonC); + + // Pressing up should go to a. + ReactTestUtils.Simulate.keyDown(focusZone, { which: KeyCodes.up }); + expect(lastFocusedElement).toBe(buttonA); + }); + it('correctly skips data-not-focusable elements', () => { const component = ReactTestUtils.renderIntoDocument(
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 6823e02c0e7b1d..68dac458c28098 100644 --- a/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusZone/FocusZone.tsx @@ -146,6 +146,7 @@ export class FocusZone extends BaseComponent implements IFo aria-describedby={ ariaDescribedBy } onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } + onClick={ this._onClick } onMouseDownCapture={ this._onMouseDown } > { this.props.children } @@ -212,23 +213,34 @@ export class FocusZone extends BaseComponent implements IFo private _onFocus = (ev: React.FocusEvent): void => { const { onActiveElementChanged } = this.props; - if (this._isImmediateDescendantOfZone(ev.target as HTMLElement)) { - this._activeElement = ev.target as HTMLElement; - this._setFocusAlignment(this._activeElement); + this._setTargetElement(ev.target, false); + if (onActiveElementChanged) { + onActiveElementChanged(this._activeElement as HTMLElement, ev); + } + } + + private _onClick = (ev: React.MouseEvent): void => { + this._setTargetElement(ev.target, true); + } + + private _setTargetElement = (target: EventTarget, forceUpdateAlignment?: boolean): void => { + const { onActiveElementChanged } = this.props; + + if (this._isImmediateDescendantOfZone(target as HTMLElement)) { + this._activeElement = target as HTMLElement; + this._setFocusAlignment(this._activeElement, forceUpdateAlignment, forceUpdateAlignment); } else { - let parentElement = ev.target as HTMLElement; + let parentElement = target as HTMLElement; while (parentElement && parentElement !== this._root.value) { if (isElementTabbable(parentElement) && this._isImmediateDescendantOfZone(parentElement)) { this._activeElement = parentElement; + this._setFocusAlignment(this._activeElement, forceUpdateAlignment, forceUpdateAlignment); break; } parentElement = getParent(parentElement, ALLOW_VIRTUAL_ELEMENTS) as HTMLElement; } } - if (onActiveElementChanged) { - onActiveElementChanged(this._activeElement as HTMLElement, ev); - } } /** diff --git a/packages/office-ui-fabric-react/src/components/Nav/__snapshots__/Nav.test.tsx.snap b/packages/office-ui-fabric-react/src/components/Nav/__snapshots__/Nav.test.tsx.snap index b1e5e6fd2fb617..fbbe421b3f491d 100644 --- a/packages/office-ui-fabric-react/src/components/Nav/__snapshots__/Nav.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/Nav/__snapshots__/Nav.test.tsx.snap @@ -6,6 +6,7 @@ exports[`Nav renders Nav correctly 1`] = ` aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone0" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/Pivot/__snapshots__/Pivot.test.tsx.snap b/packages/office-ui-fabric-react/src/components/Pivot/__snapshots__/Pivot.test.tsx.snap index 225f07e583fc7f..3b4aa053821ef3 100644 --- a/packages/office-ui-fabric-react/src/components/Pivot/__snapshots__/Pivot.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/Pivot/__snapshots__/Pivot.test.tsx.snap @@ -7,6 +7,7 @@ exports[`Pivot renders Pivot correctly 1`] = ` aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone1" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/Rating/__snapshots__/Rating.test.tsx.snap b/packages/office-ui-fabric-react/src/components/Rating/__snapshots__/Rating.test.tsx.snap index 686bdd8cd92670..a93e9308308de3 100644 --- a/packages/office-ui-fabric-react/src/components/Rating/__snapshots__/Rating.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/Rating/__snapshots__/Rating.test.tsx.snap @@ -18,6 +18,7 @@ exports[`Rating Renders Rating correctly 1`] = ` } data-focuszone-id="FocusZone2" data-is-focusable={false} + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/SwatchColorPicker/__snapshots__/SwatchColorPicker.test.tsx.snap b/packages/office-ui-fabric-react/src/components/SwatchColorPicker/__snapshots__/SwatchColorPicker.test.tsx.snap index 0a55445b0bccb3..8177cbed6ad150 100644 --- a/packages/office-ui-fabric-react/src/components/SwatchColorPicker/__snapshots__/SwatchColorPicker.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/SwatchColorPicker/__snapshots__/SwatchColorPicker.test.tsx.snap @@ -14,6 +14,7 @@ exports[`SwatchColorPicker renders SwatchColorPicker correctly 1`] = ` } data-focuszone-id="FocusZone3" onBlur={[Function]} + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} diff --git a/packages/office-ui-fabric-react/src/components/pickers/__snapshots__/BasePicker.test.tsx.snap b/packages/office-ui-fabric-react/src/components/pickers/__snapshots__/BasePicker.test.tsx.snap index 6ed2487d4f220f..6ba81af3ad968b 100644 --- a/packages/office-ui-fabric-react/src/components/pickers/__snapshots__/BasePicker.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/pickers/__snapshots__/BasePicker.test.tsx.snap @@ -10,6 +10,7 @@ exports[`Pickers BasePicker renders BasePicker correctly 1`] = ` aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone0" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]} @@ -77,6 +78,7 @@ exports[`Pickers TagPicker renders TagPicker correctly 1`] = ` aria-labelledby={undefined} className="ms-FocusZone" data-focuszone-id="FocusZone17" + onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseDownCapture={[Function]}