Skip to content
Merged
8 changes: 6 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,12 @@ jobs:

# only run e2e tests when the appropriate storybook is published by scoping to relevant packages
- script: |
yarn e2e $(sinceArg) --scope @fluentui/react-components --scope @fluentui/react
Copy link
Member Author

Choose a reason for hiding this comment

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

I think having two packages as scopes caused this PR's first build to try and run tests for deps of react-components even though react-components hadn't been built or deployed. Hopefully splitting into two steps will fix it. (Problem is it won't be possible to tell in this PR, because changing a pipeline file makes the PR build all packages.)

displayName: Cypress E2E tests
yarn e2e $(sinceArg) --scope @fluentui/react-components
displayName: v9 Cypress E2E tests

- script: |
yarn e2e $(sinceArg) --scope @fluentui/react
displayName: v8 Cypress E2E tests

- template: .devops/templates/cleanup.yml

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as React from 'react';
import { FocusTrapZone, mergeStyles } from '@fluentui/react';
import { useGlobal } from './shared';

// make the example a little easier to visually follow when debugging
const rootClass = mergeStyles({
'> div': {
// target all child FTZ roots
border: '2px dashed blue',
padding: 10,
margin: 10,
button: {
display: 'inline-block',
marginLeft: 10,
},
},
'*:focus': { outline: '2px dashed red' },
});

/**
* It maintains a proper stack of FocusTrapZones as more are mounted/unmounted
*/
export const FocusStack = () => {
// Whether to render each FocusTrapZone
const [shouldRender, setShouldRender] = React.useState([true, false, false, false, false]);

const updateFTZ = (num: 1 | 2 | 3 | 4, newValue: boolean) => {
setShouldRender(prevValues => {
const newValues = [...prevValues];
newValues[num] = newValue;
return newValues;
});
};

useGlobal('getFocusStack', () => FocusTrapZone.focusStack);

return (
<div className={rootClass}>
<FocusTrapZone id="ftz0">
ftz0
<button onClick={() => updateFTZ(1, true)}>add ftz1</button>
<button onClick={() => updateFTZ(3, true)}>add ftz3</button>
<button onClick={() => updateFTZ(4, true)}>add ftz4</button>
</FocusTrapZone>

{shouldRender[1] && (
<FocusTrapZone id="ftz1">
ftz1
<button onClick={() => updateFTZ(2, true)}>add ftz2</button>
</FocusTrapZone>
)}
{shouldRender[2] && (
<FocusTrapZone id="ftz2">
ftz2
<button onClick={() => updateFTZ(1, false)}>remove ftz1</button>
<button onClick={() => updateFTZ(2, false)}>remove ftz2</button>
</FocusTrapZone>
)}
{shouldRender[3] && (
<FocusTrapZone id="ftz3" forceFocusInsideTrap={false}>
ftz3
<button onClick={() => updateFTZ(3, false)}>remove ftz3</button>
</FocusTrapZone>
)}
{shouldRender[4] && (
<FocusTrapZone id="ftz4" disabled>
ftz4
</FocusTrapZone>
)}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from 'react';
import { FocusZone, FocusTrapZone, mergeStyles, IFocusTrapZone } from '@fluentui/react';
import { useGlobal, useProps } from './shared';

const rootClass = mergeStyles({
button: {
height: 30,
width: 60,
display: 'block',
},
'*:focus': { outline: '2px dashed red' },
});

/** Imperatively focusing the FTZ */
export const ImperativeFocus = () => {
const props = useProps();
const focusTrapZoneRef = React.useRef<IFocusTrapZone>(null);

useGlobal('imperativeFocus', () => focusTrapZoneRef.current?.focus());

return (
// don't render until props have been set
props && (
<div className={rootClass}>
<FocusTrapZone disableFirstFocus componentRef={focusTrapZoneRef} {...props}>
<button>first</button>
<FocusZone>
<button>mid</button>
<button>last</button>
</FocusZone>
</FocusTrapZone>
<button>after</button>
</div>
)
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from 'react';
import { FocusTrapZone, mergeStyles } from '@fluentui/react';
import { useProps } from './shared';

const rootClass = mergeStyles({
button: { height: 30, width: 60 },
'*:focus': { outline: '2px dashed red' },
});

/**
* Tab and shift-tab when the FTZ contains 0 tabbable items
*/
export const NoTabbableItems = () => {
const props = useProps();

return (
// don't render until props have been set
props && (
<div className={rootClass}>
<button>before</button>
<FocusTrapZone forceFocusInsideTrap {...props}>
<button tabIndex={-1}>first</button>
<button tabIndex={-1}>mid</button>
<button tabIndex={-1}>last</button>
</FocusTrapZone>
<button>after</button>
</div>
)
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react';
import { FocusTrapZone, mergeStyles } from '@fluentui/react';
import { useProps } from './shared';

const rootClass = mergeStyles({
button: { height: 30, width: 60, display: 'block' },
'*:focus': { outline: '2px dashed red' },
});

/** Respects default and explicit prop values */
export const PropValues = () => {
const [buttonClicked, setButtonClicked] = React.useState('');
const props = useProps();

return (
// don't render until props have been set
props && (
<div className={rootClass} onClick={ev => setButtonClicked((ev.target as HTMLButtonElement).textContent || '')}>
<div id="buttonClicked">clicked {buttonClicked}</div>
<button>before</button>
<FocusTrapZone {...props}>
<button>first</button>
<button>mid</button>
<button className="last-class" id="last">
last
</button>
</FocusTrapZone>
<button>after</button>
</div>
)
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from 'react';
import { FocusZone, FocusTrapZone, FocusZoneDirection, mergeStyles } from '@fluentui/react';

const rootClass = mergeStyles({
button: { height: 30, width: 60 },
'*:focus': { outline: '2px dashed red' },
});

/**
* Tab and shift-tab wrap at extreme ends of the FTZ:
*
* can tab between a button and a FocusZone
*/
export const TabWrappingButtonFocusZone = () => {
return (
<div className={rootClass}>
<FocusTrapZone forceFocusInsideTrap={false}>
<div>
<button>first</button>
</div>
<FocusZone direction={FocusZoneDirection.horizontal}>
<div>
<button>fzFirst</button>
</div>
<div>
<div>
<button>fzMid1</button>
<button>fzMid2</button>
<button>fzLast</button>
</div>
</div>
</FocusZone>
</FocusTrapZone>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';
import { FocusZone, FocusTrapZone, FocusZoneDirection, mergeStyles } from '@fluentui/react';

const rootClass = mergeStyles({
button: { width: 60, height: 30 },
'*:focus': { outline: '2px dashed red' },
});

/**
* Tab and shift-tab wrap at extreme ends of the FTZ:
*
* can trap focus when FTZ bookmark elements are FocusZones,
* and those elements have inner elements focused that are not the first inner element
*/
export const TabWrappingFocusZoneBumpers = () => {
return (
<div className={rootClass}>
<button>before</button>
<FocusTrapZone forceFocusInsideTrap={false}>
<FocusZone direction={FocusZoneDirection.horizontal}>
<button>fz1First</button>
<button>fz1Mid</button>
<button>fz1Last</button>
</FocusZone>
<button>mid</button>
<FocusZone direction={FocusZoneDirection.horizontal}>
<button>fz2First</button>
<button>fz2Mid</button>
<button>fz2Last</button>
</FocusZone>
</FocusTrapZone>
<button>after</button>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,36 @@ import * as React from 'react';
import { FocusZone, FocusTrapZone, FocusZoneDirection, mergeStyles } from '@fluentui/react';

const rootClass = mergeStyles({
position: 'relative',
button: { position: 'absolute', height: 30, width: 30 },
'#a': { top: 0, left: 0 },
'#b': { top: 0, left: 30 },
'#c': { top: 0, left: 60 },
'#d': { top: 30, left: 0 },
'#e': { top: 30, left: 30 },
'#f': { top: 30, left: 60 },
button: { height: 30, width: 60 },
'*:focus': { outline: '2px dashed red' },
});

/**
* Tab and shift-tab wrap at extreme ends of the FTZ:
*
* can tab across FocusZones with different button structures
* can between multiple FocusZones with different button structures
Copy link
Member

@khmakoto khmakoto Mar 2, 2022

Choose a reason for hiding this comment

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

nit:

Suggested change
* can between multiple FocusZones with different button structures
* can tab between multiple FocusZones with different button structures

*/
export const TabWrappingMultiFocusZone = () => {
return (
<div className={rootClass}>
<FocusTrapZone forceFocusInsideTrap={false}>
<FocusZone direction={FocusZoneDirection.horizontal}>
<div>
<button id="a">a</button>
<button>fz1First</button>
</div>
<div>
<button id="b">b</button>
<button>fz1Mid</button>
</div>
<div>
<button id="c">c</button>
<button>fz1Last</button>
</div>
</FocusZone>
<FocusZone direction={FocusZoneDirection.horizontal}>
<div>
<div>
<button id="d">d</button>
<button id="e">e</button>
<button id="f">f</button>
<button>fz2First</button>
<button>fz2Mid</button>
<button>fz2Last</button>
</div>
</div>
</FocusZone>
Expand Down
Loading