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
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..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
@@ -704,6 +704,77 @@ 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(buttonA.getAttribute('tabindex')).toBe('-1');
+ expect(buttonB.getAttribute('tabindex')).toBe('0');
+ 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(
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..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,11 +308,19 @@ 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 || initialElementFocused) {
+ this._setFocusAlignment(newActiveElement, initialElementFocused);
+ }
+
this._activeElement = newActiveElement;
- if (isImmediateDescendant) {
- this._setFocusAlignment(this._activeElement);
+ if (initialElementFocused) {
+ this._updateTabIndexes();
}
}
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
};
}
}
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.
-