Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,77 @@ describe('FocusZone', () => {
setRTL(false);
});

it('can focus correctly when receiving initial focus, bidirectionally', () => {
const component = ReactTestUtils.renderIntoDocument(
<div {...{ onFocusCapture: _onFocus }}>
<FocusZone>
<button className="a">a</button>
<button className="b">b</button>
<button className="c">c</button>
<button className="d">d</button>
</FocusZone>
</div>
);

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, is this actually meant to overlap with Button B? (Button B's left is 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(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(
<div {...{ onFocusCapture: _onFocus }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,19 @@ export class FocusZone extends React.Component<IFocusZoneProps, {}> 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();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export interface IPhoto {
url: string;
width: number;
height: number;
onClick: (ev: React.MouseEvent<HTMLElement>) => void;
}

export class FocusZonePhotosExample extends React.Component<{}, { items: IPhoto[] }> {
Expand All @@ -27,7 +26,6 @@ export class FocusZonePhotosExample extends React.Component<{}, { items: IPhoto[

return (
<FocusZone as="ul" className="ms-FocusZoneExamples-photoList">
<p>Note: clicking on the items below will remove them, illustrating how FocusZone will retain focus even when items are removed.</p>
{items.map((item: IPhoto, index) => (
<li
key={item.id}
Expand All @@ -36,7 +34,6 @@ export class FocusZonePhotosExample extends React.Component<{}, { items: IPhoto[
aria-setsize={items.length}
aria-label="Photo"
data-is-focusable={true}
onClick={item.onClick}
>
<Image src={item.url} width={item.width} height={item.height} />
</li>
Expand All @@ -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<HTMLElement>) => {
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
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,12 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
onMouseDownCapture={[Function]}
role="presentation"
>
<p>
Note: clicking on the items below will remove them, illustrating how FocusZone will retain focus even when items are removed.
</p>
<li
aria-label="Photo"
aria-posinset={1}
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -62,7 +58,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -106,7 +101,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -150,7 +144,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -194,7 +187,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -238,7 +230,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -282,7 +273,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -326,7 +316,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -370,7 +359,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -414,7 +402,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -458,7 +445,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -502,7 +488,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -546,7 +531,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -590,7 +574,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -634,7 +617,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -678,7 +660,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -722,7 +703,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -766,7 +746,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -810,7 +789,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down Expand Up @@ -854,7 +832,6 @@ exports[`Component Examples renders FocusZone.Photos.Example.tsx correctly 1`] =
aria-setsize={20}
className="ms-FocusZoneExamples-photoCell"
data-is-focusable={true}
onClick={[Function]}
>
<div
className=
Expand Down