Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
de2ca14
undo.
dzearing Mar 14, 2018
e77281d
initial scaffolding
dzearing Jan 17, 2019
c5515f8
formatting
dzearing Jan 17, 2019
a6069ed
Merge branch 'master' of https://github.com/OfficeDev/office-ui-fabri…
dzearing Jan 17, 2019
aa2c6b1
updates
dzearing Jan 22, 2019
4cdb158
Tests are working.
dzearing Jan 24, 2019
a0404aa
Merge branch 'focuszone' of https://github.com/dzearing/office-ui-fab…
dzearing Jan 25, 2019
f9ee9dd
Merge branch 'master' of https://github.com/OfficeDev/office-ui-fabri…
dzearing Jan 28, 2019
6edeaf2
changes.
dzearing Jan 28, 2019
66ab5e3
Changes
dzearing Jan 28, 2019
c47a3a1
undo ftz test issue.
dzearing Jan 28, 2019
a545c20
Updates to tests.
dzearing Jan 28, 2019
f11300a
api
dzearing Jan 28, 2019
a8e8b9c
Only restore focus on unpark when needed.
dzearing Jan 29, 2019
80c400e
Updates
dzearing Jan 30, 2019
8bedfd4
adjustments
dzearing Jan 30, 2019
d38574a
Updates to the photos example (you can click to remove a photo) and f…
dzearing Jan 30, 2019
e1ed53a
Fixign build.
dzearing Jan 30, 2019
70b24fb
More updates
dzearing Jan 30, 2019
b98cba9
updating snapshots.
dzearing Jan 30, 2019
7b9e54b
Moving utils to utils.
dzearing Jan 31, 2019
1e67449
updates to api.
dzearing Jan 31, 2019
be3f29b
Adding tests.
dzearing Jan 31, 2019
8832984
undo merge issues.
dzearing Jan 31, 2019
33e187d
woot
dzearing Jan 31, 2019
f85f09e
Snapshot updates.
dzearing Jan 31, 2019
65db0c3
Merge branch 'master' of https://github.com/OfficeDev/office-ui-fabri…
dzearing Jan 31, 2019
4b12dc4
fix
dzearing Jan 31, 2019
c44e894
Adding comments on park helper.
dzearing Jan 31, 2019
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": [
{
"comment": "",
"packageName": "@uifabric/utilities",
"type": "none"
}
],
"packageName": "@uifabric/utilities",
"email": "dzearing@microsoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "FocusZone: focus can now be recovered if focus was resting within the zone but the element was removed.",
"type": "minor"
}
],
"packageName": "office-ui-fabric-react",
"email": "dzearing@microsoft.com"
}
3 changes: 2 additions & 1 deletion packages/office-ui-fabric-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@uifabric/jest-serializer-merge-styles": ">=6.0.7 <7.0.0",
"@uifabric/prettier-rules": ">=1.0.0 <2.0.0",
"@uifabric/tslint-rules": ">=1.0.0 <2.0.0",
"@uifabric/test-utilities": ">=6.0.0 <7.0.0",
"@uifabric/webpack-utils": ">=0.7.4 <1.0.0",
"enzyme": "^3.4.1",
"enzyme-adapter-react-16": "^1.2.0",
Expand Down Expand Up @@ -76,4 +77,4 @@
"react": ">=16.3.2-0 <17.0.0",
"react-dom": ">=16.3.2-0 <17.0.0"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -277,220 +277,4 @@ describe('FocusTrapZone', () => {
expect(lastFocusedElement).toBe(buttonF);
});
});

describe('Tab and shift-tab do nothing (keep focus where it is) when the FTZ contains 0 tabbable items', () => {
Comment thread
dzearing marked this conversation as resolved.
function setupTest() {
const topLevelDiv = ReactTestUtils.renderIntoDocument(
<div onFocusCapture={_onFocus}>
<button className={'z1'}>z1</button>
<FocusTrapZone forceFocusInsideTrap={false}>
<button className={'a'} tabIndex={-1}>
a
</button>
<button className={'b'} tabIndex={-1}>
b
</button>
<button className={'c'} tabIndex={-1}>
c
</button>
</FocusTrapZone>
<button className={'z2'}>z2</button>
</div>
) as HTMLElement;

const buttonZ1 = topLevelDiv.querySelector('.z1') as HTMLElement;
const buttonA = topLevelDiv.querySelector('.a') as HTMLElement;
const buttonB = topLevelDiv.querySelector('.b') as HTMLElement;
const buttonC = topLevelDiv.querySelector('.c') as HTMLElement;
const buttonZ2 = topLevelDiv.querySelector('.z2') as HTMLElement;

// Assign bounding locations to buttons.
setupElement(buttonZ1, { clientRect: { top: 0, bottom: 10, left: 0, right: 10 } });
setupElement(buttonA, { clientRect: { top: 10, bottom: 20, left: 0, right: 10 } });
setupElement(buttonB, { clientRect: { top: 20, bottom: 30, left: 0, right: 10 } });
setupElement(buttonC, { clientRect: { top: 30, bottom: 40, left: 0, right: 10 } });
setupElement(buttonZ2, { clientRect: { top: 40, bottom: 50, left: 0, right: 10 } });

return { buttonZ1, buttonA, buttonB, buttonC, buttonZ2 };
}

it('does not move when pressing tab', async () => {
expect.assertions(2);

const { buttonB } = setupTest();

// Focus the middle button in the FTZ, even though it has tabIndex=-1
ReactTestUtils.Simulate.focus(buttonB);
await animationFrame();
expect(lastFocusedElement).toBe(buttonB);

// Pressing tab should stay where you are.
ReactTestUtils.Simulate.keyDown(buttonB, { which: KeyCodes.tab });
await animationFrame();
expect(lastFocusedElement).toBe(buttonB);
});

it('does not move when pressing shift-tab', async () => {
expect.assertions(2);

const { buttonB } = setupTest();

// Focus the middle button in the FTZ, even though it has tabIndex=-1
ReactTestUtils.Simulate.focus(buttonB);
await animationFrame();
expect(lastFocusedElement).toBe(buttonB);

// Pressing shift-tab should stay where you are.
ReactTestUtils.Simulate.keyDown(buttonB, { which: KeyCodes.tab, shiftKey: true });
await animationFrame();
expect(lastFocusedElement).toBe(buttonB);
});
});

describe('Focusing the FTZ', () => {
function setupTest(focusPreviouslyFocusedInnerElement: boolean) {
const focusTrapZoneRef = React.createRef<FocusTrapZone>();
const topLevelDiv = ReactTestUtils.renderIntoDocument(
<div onFocusCapture={_onFocus}>
<FocusTrapZone
forceFocusInsideTrap={false}
focusPreviouslyFocusedInnerElement={focusPreviouslyFocusedInnerElement}
data-is-focusable={true}
ref={focusTrapZoneRef}
>
<button className={'f'}>f</button>
<FocusZone>
<button className={'a'}>a</button>
<button className={'b'}>b</button>
</FocusZone>
</FocusTrapZone>
<button className={'z'}>z</button>
</div>
) as HTMLElement;

const buttonF = topLevelDiv.querySelector('.f') as HTMLElement;
const buttonA = topLevelDiv.querySelector('.a') as HTMLElement;
const buttonB = topLevelDiv.querySelector('.b') as HTMLElement;
const buttonZ = topLevelDiv.querySelector('.z') as HTMLElement;

// Assign bounding locations to buttons.
setupElement(buttonF, { clientRect: { top: 0, bottom: 10, left: 0, right: 10 } });
setupElement(buttonA, { clientRect: { top: 10, bottom: 20, left: 0, right: 10 } });
setupElement(buttonB, { clientRect: { top: 20, bottom: 30, left: 0, right: 10 } });
setupElement(buttonZ, { clientRect: { top: 30, bottom: 40, left: 0, right: 10 } });

return { focusTrapZone: focusTrapZoneRef.current!, buttonF, buttonA, buttonB, buttonZ };
}

it('goes to previously focused element when focusing the FTZ', async () => {
expect.assertions(4);

const { focusTrapZone, buttonF, buttonB, buttonZ } = setupTest(true /*focusPreviouslyFocusedInnerElement*/);

// Manually focusing FTZ when FTZ has never
// had focus within should go to 1st focusable inner element.
focusTrapZone.focus();
await animationFrame();
expect(lastFocusedElement).toBe(buttonF);

// Focus inside the trap zone, not the first element.
ReactTestUtils.Simulate.focus(buttonB);
await animationFrame();
expect(lastFocusedElement).toBe(buttonB);

// Focus outside the trap zone
ReactTestUtils.Simulate.focus(buttonZ);
await animationFrame();
expect(lastFocusedElement).toBe(buttonZ);

// Manually focusing FTZ should return to originally focused inner element.
focusTrapZone.focus();
await animationFrame();
expect(lastFocusedElement).toBe(buttonB);
});

it('goes to first focusable element when focusing the FTZ', async () => {
expect.assertions(4);

const { focusTrapZone, buttonF, buttonB, buttonZ } = setupTest(false /*focusPreviouslyFocusedInnerElement*/);

// Manually focusing FTZ when FTZ has never
// had focus within should go to 1st focusable inner element.
focusTrapZone.focus();
await animationFrame();
expect(lastFocusedElement).toBe(buttonF);

// Focus inside the trap zone, not the first element.
ReactTestUtils.Simulate.focus(buttonB);
await animationFrame();
expect(lastFocusedElement).toBe(buttonB);

// Focus outside the trap zone
ReactTestUtils.Simulate.focus(buttonZ);
await animationFrame();
expect(lastFocusedElement).toBe(buttonZ);

// Manually focusing FTZ should go to the first focusable element.
focusTrapZone.focus();
await animationFrame();
expect(lastFocusedElement).toBe(buttonF);
});
});

describe('Nested FocusTrapZones Stack Behavior', () => {
function getFocusStack(): FocusTrapZone[] {
return (FocusTrapZone as any)._focusStack;
}

beforeAll(() => {
getFocusStack().length = 0;
});

it('FocusTrapZone maintains a proper stack of FocusTrapZones as more are mounted/unmounted.', async () => {
let focusTrapZoneFocusStack: FocusTrapZone[] = getFocusStack();
const topLevelDiv = ReactTestUtils.renderIntoDocument(
<div>
<FocusTrapZoneTestComponent />
</div>
) as HTMLElement;
const buttonA = topLevelDiv.querySelector('.a') as HTMLElement;

const buttonB = topLevelDiv.querySelector('.b') as HTMLElement;

expect(focusTrapZoneFocusStack.length).toBe(2);
const baseFocusTrapZone = focusTrapZoneFocusStack[0];
expect(baseFocusTrapZone.props.forceFocusInsideTrap).toBe(true);
expect(baseFocusTrapZone.props.isClickableOutsideFocusTrap).toBe(false);

const firstFocusTrapZone = focusTrapZoneFocusStack[1];
expect(firstFocusTrapZone.props.forceFocusInsideTrap).toBe(false);
expect(firstFocusTrapZone.props.isClickableOutsideFocusTrap).toBe(false);

// There should be now 3 focus trap zones (base/first/second)
ReactTestUtils.Simulate.click(buttonB);
expect(focusTrapZoneFocusStack.length).toBe(3);
expect(focusTrapZoneFocusStack[0]).toBe(baseFocusTrapZone);
expect(focusTrapZoneFocusStack[1]).toBe(firstFocusTrapZone);
const secondFocusTrapZone = focusTrapZoneFocusStack[2];
expect(secondFocusTrapZone.props.forceFocusInsideTrap).toBe(false);
expect(secondFocusTrapZone.props.isClickableOutsideFocusTrap).toBe(true);

// we remove the middle one
// unmounting a focus trap zone should remove it from the focus stack.
// but we also check that it removes the right focustrapzone (the middle one)
ReactTestUtils.Simulate.click(buttonA);
focusTrapZoneFocusStack = getFocusStack();

expect(focusTrapZoneFocusStack.length).toBe(2);
expect(focusTrapZoneFocusStack[0]).toBe(baseFocusTrapZone);
expect(focusTrapZoneFocusStack[1]).toBe(secondFocusTrapZone);

// finally remove the last focus trap zone.
ReactTestUtils.Simulate.click(buttonB);
focusTrapZoneFocusStack = getFocusStack();

expect(focusTrapZoneFocusStack.length).toBe(1);
expect(focusTrapZoneFocusStack[0]).toBe(baseFocusTrapZone);
});
});
});
Loading