diff --git a/common/changes/office-ui-fabric-react/jg-ftz-disabled-prop_2019-04-22-23-09.json b/common/changes/office-ui-fabric-react/jg-ftz-disabled-prop_2019-04-22-23-09.json new file mode 100644 index 0000000000000..e10810e6f54c8 --- /dev/null +++ b/common/changes/office-ui-fabric-react/jg-ftz-disabled-prop_2019-04-22-23-09.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "office-ui-fabric-react", + "comment": "FocusTrapZone: Add disabled prop.", + "type": "minor" + } + ], + "packageName": "office-ui-fabric-react", + "email": "jagore@microsoft.com" +} \ No newline at end of file diff --git a/packages/office-ui-fabric-react/etc/office-ui-fabric-react.api.md b/packages/office-ui-fabric-react/etc/office-ui-fabric-react.api.md index 1db9733ee3dc7..ce3e93cc7afea 100644 --- a/packages/office-ui-fabric-react/etc/office-ui-fabric-react.api.md +++ b/packages/office-ui-fabric-react/etc/office-ui-fabric-react.api.md @@ -4225,6 +4225,7 @@ export interface IFocusTrapZone { export interface IFocusTrapZoneProps extends React.HTMLAttributes { ariaLabelledBy?: string; componentRef?: IRefObject; + disabled?: boolean; disableFirstFocus?: boolean; elementToFocusOnDismiss?: HTMLElement; firstFocusableSelector?: string | (() => string); diff --git a/packages/office-ui-fabric-react/src/components/Callout/FocusTrapCallout.tsx b/packages/office-ui-fabric-react/src/components/Callout/FocusTrapCallout.tsx index 99c507688d398..c06d7aa017c25 100644 --- a/packages/office-ui-fabric-react/src/components/Callout/FocusTrapCallout.tsx +++ b/packages/office-ui-fabric-react/src/components/Callout/FocusTrapCallout.tsx @@ -11,7 +11,9 @@ import { FocusTrapZone } from '../../FocusTrapZone'; export const FocusTrapCallout: React.StatelessComponent = (props: IFocusTrapCalloutProps): JSX.Element => { return ( - {props.children} + + {props.children} + ); }; diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.doc.tsx b/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.doc.tsx index f38ec14542b0d..2b9f37363c1de 100644 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.doc.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.doc.tsx @@ -18,10 +18,6 @@ import { FocusTrapZoneNestedExample } from './examples/FocusTrapZone.Nested.Exam const FocusTrapZoneNestedExampleCode = require('!raw-loader!office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Nested.Example.tsx') as string; const FocusTrapZoneNestedExampleCodepen = require('!@uifabric/codepen-loader!office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Nested.Example.tsx') as string; -import { FocusTrapZoneNoTabbableExample } from './examples/FocusTrapZone.NoTabbable.Example'; -const FocusTrapZoneNoTabbableExampleCode = require('!raw-loader!office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.NoTabbable.Example.tsx') as string; -const FocusTrapZoneNoTabbableExampleCodepen = require('!@uifabric/codepen-loader!office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.NoTabbable.Example.tsx') as string; - import { FocusTrapZoneFocusZoneExample } from './examples/FocusTrapZone.FocusZone.Example'; const FocusTrapZoneFocusZoneExampleCode = require('!raw-loader!office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.FocusZone.Example.tsx') as string; const FocusTrapZoneFocusZoneExampleCodepen = require('!@uifabric/codepen-loader!office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.FocusZone.Example.tsx') as string; @@ -66,12 +62,6 @@ export const FocusTrapZonePageProps: IDocPageProps = { codepenJS: FocusTrapZoneFocusZoneExampleCodepen, view: }, - { - title: 'FocusTrapZone with no tabbable elements', - code: FocusTrapZoneNoTabbableExampleCode, - codepenJS: FocusTrapZoneNoTabbableExampleCodepen, - view: - }, { title: 'A Dialog nested in a Panel', code: FocusTrapZoneDialogInPanelExampleCode, diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.test.tsx b/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.test.tsx index dec6135099c6f..613fa6afbc0ac 100644 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.test.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.test.tsx @@ -579,6 +579,19 @@ describe('FocusTrapZone', () => { expect(document.activeElement).toBe(activeElement); }); + it('Does not focus first on mount while disabled', async () => { + expect.assertions(1); + + const activeElement = document.activeElement; + + setupTest({ disabled: true }); + + // document.activeElement can be used to detect activeElement after component mount, but it does not + // update based on focus events due to limitations of ReactDOM. + // Make sure activeElement didn't change. + expect(document.activeElement).toBe(activeElement); + }); + it('Focuses on firstFocusableSelector on mount', async () => { expect.assertions(1); @@ -587,6 +600,16 @@ describe('FocusTrapZone', () => { expect(document.activeElement).toBe(buttonC); }); + it('Does not focus on firstFocusableSelector on mount while disabled', async () => { + expect.assertions(1); + + const activeElement = document.activeElement; + + setupTest({ firstFocusableSelector: 'c', disabled: true }); + + expect(document.activeElement).toBe(activeElement); + }); + it('Falls back to first focusable element with invalid firstFocusableSelector', async () => { const { buttonA } = setupTest({ firstFocusableSelector: 'invalidSelector' }); diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.tsx b/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.tsx index d2faefa5c7a88..7da1c3a7daba1 100644 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.tsx @@ -47,22 +47,28 @@ export class FocusTrapZone extends React.Component impl public componentDidUpdate(prevProps: IFocusTrapZoneProps) { const prevForceFocusInsideTrap = prevProps.forceFocusInsideTrap !== undefined ? prevProps.forceFocusInsideTrap : true; const newForceFocusInsideTrap = this.props.forceFocusInsideTrap !== undefined ? this.props.forceFocusInsideTrap : true; + const prevDisabled = prevProps.disabled !== undefined ? prevProps.disabled : false; + const newDisabled = this.props.disabled !== undefined ? this.props.disabled : false; - if (!prevForceFocusInsideTrap && newForceFocusInsideTrap) { - // Transition from forceFocusInsideTrap disabled to enabled. Emulate what happens when a FocusTrapZone gets mounted + if ((!prevForceFocusInsideTrap && newForceFocusInsideTrap) || (prevDisabled && !newDisabled)) { + // Transition from forceFocusInsideTrap / FTZ disabled to enabled. + // Emulate what happens when a FocusTrapZone gets mounted. this._bringFocusIntoZone(); - } else if (prevForceFocusInsideTrap && !newForceFocusInsideTrap) { - // Transition from forceFocusInsideTrap enabled to disabled. Emulate what happens when a FocusTrapZone gets unmounted + } else if ((prevForceFocusInsideTrap && !newForceFocusInsideTrap) || (!prevDisabled && newDisabled)) { + // Transition from forceFocusInsideTrap / FTZ enabled to disabled. + // Emulate what happens when a FocusTrapZone gets unmounted. this._returnFocusToInitiator(); } } public componentWillUnmount(): void { - this._returnFocusToInitiator(); + if (!this.props.disabled) { + this._returnFocusToInitiator(); + } } public render(): JSX.Element { - const { className, ariaLabelledBy } = this.props; + const { className, disabled = false, ariaLabelledBy } = this.props; const divProps = getNativeProps(this.props, divProperties); const bumperProps = { @@ -70,7 +76,7 @@ export class FocusTrapZone extends React.Component impl pointerEvents: 'none', position: 'fixed' // 'fixed' prevents browsers from scrolling to bumpers when viewport does not contain them }, - tabIndex: 0, + tabIndex: disabled ? -1 : 0, // make bumpers tabbable only when enabled 'aria-hidden': true, 'data-is-visible': true } as React.HTMLAttributes; @@ -168,7 +174,12 @@ export class FocusTrapZone extends React.Component impl }; private _onBumperFocus = (isFirstBumper: boolean) => { + if (this.props.disabled) { + return; + } + const currentBumper = (isFirstBumper === this._hasFocus ? this._lastBumper.current : this._firstBumper.current) as HTMLElement; + if (this._root.current) { const nextFocusable = isFirstBumper === this._hasFocus @@ -187,7 +198,11 @@ export class FocusTrapZone extends React.Component impl }; private _bringFocusIntoZone(): void { - const { elementToFocusOnDismiss, disableFirstFocus = false } = this.props; + const { elementToFocusOnDismiss, disabled = false, disableFirstFocus = false } = this.props; + + if (disabled) { + return; + } FocusTrapZone._focusStack.push(this); @@ -252,6 +267,10 @@ export class FocusTrapZone extends React.Component impl } private _forceFocusInTrap = (ev: FocusEvent): void => { + if (this.props.disabled) { + return; + } + if (FocusTrapZone._focusStack.length && this === FocusTrapZone._focusStack[FocusTrapZone._focusStack.length - 1]) { const focusedElement = document.activeElement as HTMLElement; @@ -265,6 +284,10 @@ export class FocusTrapZone extends React.Component impl }; private _forceClickInTrap = (ev: MouseEvent): void => { + if (this.props.disabled) { + return; + } + if (FocusTrapZone._focusStack.length && this === FocusTrapZone._focusStack[FocusTrapZone._focusStack.length - 1]) { const clickedElement = ev.target as HTMLElement; diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.types.ts b/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.types.ts index 6b8f0f5a98da1..f3c71a6def8ac 100644 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.types.ts +++ b/packages/office-ui-fabric-react/src/components/FocusTrapZone/FocusTrapZone.types.ts @@ -22,9 +22,15 @@ export interface IFocusTrapZoneProps extends React.HTMLAttributes; + /** + * Disables the FocusTrapZone's focus trapping behavior when set. + * @defaultvalue false + */ + disabled?: boolean; + /** * Sets the HTMLElement to focus on when exiting the FocusTrapZone. - * @defaultvalue The element.target that triggered the FTZ. + * @defaultvalue element.target The element.target that triggered the FTZ. */ elementToFocusOnDismiss?: HTMLElement; diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.Click.Example.tsx b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.Click.Example.tsx index 2870ca3a9ad1b..7c29bb4328e76 100644 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.Click.Example.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.Click.Example.tsx @@ -16,51 +16,33 @@ export class FocusTrapZoneBoxClickExample extends React.Component<{}, IFocusTrap private _toggle = React.createRef(); public render() { - return ( -
- {this.state.useTrapZone ? ( - - {this._internalContents()} - - ) : ( - this._internalContents() - )} -
- ); - } - - private _internalContents() { const { useTrapZone } = this.state; return ( - - - - Hyperlink inside trap zone - + + + + + Hyperlink inside trap zone + + ); } private _onFocusTrapZoneToggleChanged = (ev: React.MouseEvent, checked?: boolean): void => { - this.setState({ useTrapZone: !!checked }, () => { - // Restore focus to toggle after disabling the trap zone - // (the trap zone itself will handle initial focus when it's enabled) - if (!checked) { - this._toggle.current!.focus(); - } - }); + this.setState({ useTrapZone: !!checked }); }; } diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.Example.tsx b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.Example.tsx index 47b62fcecf8e7..dca44a9a1eea3 100644 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.Example.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.Example.tsx @@ -1,10 +1,12 @@ import * as React from 'react'; +import { DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { FocusTrapZone } from 'office-ui-fabric-react/lib/FocusTrapZone'; import { Link } from 'office-ui-fabric-react/lib/Link'; +import { Stack } from 'office-ui-fabric-react/lib/Stack'; +import { Text } from 'office-ui-fabric-react/lib/Text'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { Toggle, IToggle } from 'office-ui-fabric-react/lib/Toggle'; -import { Stack } from 'office-ui-fabric-react/lib/Stack'; export interface IFocusTrapZoneBoxExampleState { useTrapZone: boolean; @@ -18,50 +20,49 @@ export class FocusTrapZoneBoxExample extends React.Component<{}, IFocusTrapZoneB private _toggle = React.createRef(); public render() { - return ( -
- {this.state.useTrapZone ? ( - {this._internalContents()} - ) : ( - // prettier-ignore - this._internalContents() - )} -
- ); - } - - private _internalContents() { const { useTrapZone } = this.state; return ( - - - - Hyperlink inside trap zone + + + + If this button is used to enable FocusTrapZone, focus should return to this button after the FocusTrapZone is disabled. + + + + + + + + + + Hyperlink inside trap zone + + ); } - private _onFocusTrapZoneToggleChanged = (ev: React.MouseEvent, checked?: boolean): void => { - this.setState({ useTrapZone: !!checked }, () => { - // Restore focus to toggle after disabling the trap zone - // (the trap zone itself will handle initial focus when it's enabled) - if (!checked) { - this._toggle.current!.focus(); - } + private _onButtonClickHandler = (): void => { + this.setState({ + useTrapZone: true }); }; + + private _onFocusTrapZoneToggleChanged = (ev: React.MouseEvent, checked?: boolean): void => { + this.setState({ useTrapZone: !!checked }); + }; } diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.FocusOnCustomElement.Example.tsx b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.FocusOnCustomElement.Example.tsx index c558eb8731828..fe36165463702 100644 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.FocusOnCustomElement.Example.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Box.FocusOnCustomElement.Example.tsx @@ -1,10 +1,12 @@ import * as React from 'react'; +import { DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { FocusTrapZone } from 'office-ui-fabric-react/lib/FocusTrapZone'; import { Link } from 'office-ui-fabric-react/lib/Link'; +import { Stack } from 'office-ui-fabric-react/lib/Stack'; +import { Text } from 'office-ui-fabric-react/lib/Text'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { Toggle, IToggle } from 'office-ui-fabric-react/lib/Toggle'; -import { Stack } from 'office-ui-fabric-react/lib/Stack'; export interface IFocusTrapZoneBoxCustomElementExampleState { useTrapZone: boolean; @@ -20,51 +22,49 @@ export class FocusTrapZoneBoxCustomElementExample extends React.Component<{}, IF private _toggle = React.createRef(); public render() { - return ( -
- {this.state.useTrapZone ? ( - {this._internalContents()} - ) : ( - this._internalContents() - )} -
- ); - } - - private _internalContents() { const { useTrapZone } = this.state; return ( - - - - - Hyperlink which will receive initial focus when trap zone is activated - + + + If this button is used to enable FocusTrapZone, the hyperlink should be focused. + + + + + + + + + + Hyperlink which will receive initial focus when trap zone is activated + + + ); } - private _onFocusTrapZoneToggleChanged = (ev: React.MouseEvent, checked?: boolean): void => { - this.setState({ useTrapZone: !!checked }, () => { - // Restore focus to toggle after disabling the trap zone - // (the trap zone itself will handle initial focus when it's enabled) - if (!checked) { - this._toggle.current!.focus(); - } + private _onButtonClickHandler = (): void => { + this.setState({ + useTrapZone: true }); }; + + private _onFocusTrapZoneToggleChanged = (ev: React.MouseEvent, checked?: boolean): void => { + this.setState({ useTrapZone: !!checked }); + }; } diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.FocusZone.Example.tsx b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.FocusZone.Example.tsx index 3f96bbc7560a5..d0dd9ed2249ee 100644 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.FocusZone.Example.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.FocusZone.Example.tsx @@ -18,20 +18,6 @@ export class FocusTrapZoneFocusZoneExample extends React.Component<{}, IFocusTra private _toggle = React.createRef(); public render() { - return ( -
- {this.state.useTrapZone ? ( - - {this._internalContents()} - - ) : ( - this._internalContents() - )} -
- ); - } - - private _internalContents() { const { useTrapZone } = this.state; const padding = 10; const border = '2px dashed #ababab'; @@ -39,34 +25,36 @@ export class FocusTrapZoneFocusZoneExample extends React.Component<{}, IFocusTra const tokens: IStackTokens = { childrenGap: 10 }; return ( - - + + + - - - - - - - + + + + + + + - + - - - - - - - - + + + + + + + + + ); } diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Nested.Example.tsx b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Nested.Example.tsx index 9c72a6509960e..5d039e9225fe5 100644 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Nested.Example.tsx +++ b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.Nested.Example.tsx @@ -14,35 +14,32 @@ interface IFocusTrapComponentProps { class FocusTrapComponent extends React.Component { public render() { const { isActive, zoneNumber, children } = this.props; - const contents = ( - - + = 2 && zoneNumber <= 4 && { width: 200 } + root: { border: `2px solid ${isActive ? '#ababab' : 'transparent'}`, padding: 10 } }} - /> - - {children} - + > + = 2 && zoneNumber <= 4 && { width: 200 } + }} + /> + + {children} + + ); - - if (this.props.isActive) { - return {contents}; - } - return contents; } private _onStringButtonClicked = (): void => { @@ -86,6 +83,12 @@ export class FocusTrapZoneNestedExample extends React.Component<{}, IFocusTrapZo this.setState({ activeStates: { ...activeStates, [zoneNumber]: isActive } }); }; + // This randomize example is exposing a quirk in focus stack behavior. + // For the randomize example, components render from the bottom up with all of the new "activeStates" simultaneously set. + // The most recently active item in the focusStack ends up being the highest parent, which is the reverse order focus + // trap zones would normally be put on the focusStack. That means children aren't capturing focus as one would normally + // expect when toggling the FTZ's individually. This would also be an issue if anyone ever rendered multiple nested and enabled + // FocusTrapZones simultaneously. private _randomize = (): void => { const activeStates: IFocusTrapZoneNestedExampleState['activeStates'] = {}; [1, 2, 3, 4, 5].forEach(zoneNumber => { diff --git a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.NoTabbable.Example.tsx b/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.NoTabbable.Example.tsx deleted file mode 100644 index 808f2be2cd55d..0000000000000 --- a/packages/office-ui-fabric-react/src/components/FocusTrapZone/examples/FocusTrapZone.NoTabbable.Example.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from 'react'; - -import { FocusTrapZone } from 'office-ui-fabric-react/lib/FocusTrapZone'; -import { TextField } from 'office-ui-fabric-react/lib/TextField'; -import { Toggle, IToggle } from 'office-ui-fabric-react/lib/Toggle'; -import { Stack } from 'office-ui-fabric-react/lib/Stack'; - -export interface IFocusTrapZoneNoTabbableExampleState { - useTrapZone: boolean; -} - -export class FocusTrapZoneNoTabbableExample extends React.Component<{}, IFocusTrapZoneNoTabbableExampleState> { - public state: IFocusTrapZoneNoTabbableExampleState = { useTrapZone: false }; - - private _toggle = React.createRef(); - - public render() { - return ( -
- {this.state.useTrapZone ? ( - - {this._internalContents()} - - ) : ( - this._internalContents() - )} -
- ); - } - - private _internalContents() { - const { useTrapZone } = this.state; - - return ( - - - - - - - ); - } - - private _onFocusTrapZoneToggleChanged = (ev: React.MouseEvent, checked?: boolean): void => { - this.setState({ useTrapZone: !!checked }, () => { - // Restore focus to toggle after disabling the trap zone - // (the trap zone itself will handle initial focus when it's enabled) - if (!checked) { - this._toggle.current!.focus(); - } - }); - }; -} diff --git a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.Click.Example.tsx.shot b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.Click.Example.tsx.shot index 2ed16525833f2..e73090a94375e 100644 --- a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.Click.Example.tsx.shot +++ b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.Click.Example.tsx.shot @@ -1,7 +1,23 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Component Examples renders FocusTrapZone.Box.Click.Example.tsx correctly 1`] = ` -
+
+
+
`; diff --git a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.Example.tsx.shot b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.Example.tsx.shot index 1cba7b46ad1b8..b31458f09c497 100644 --- a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.Example.tsx.shot +++ b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.Example.tsx.shot @@ -1,232 +1,249 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Component Examples renders FocusTrapZone.Box.Example.tsx correctly 1`] = ` -
+
* { + text-overflow: ellipsis; + } + & > *:not(:first-child) { + margin-top: 8px; + } + & > *:not(.ms-StackItem) { + flex-shrink: 1; + } +>
* { - text-overflow: ellipsis; - } - & > *:not(:first-child) { - margin-top: 15px; - } - & > *:not(.ms-StackItem) { + > + + If this button is used to enable FocusTrapZone, focus should return to this button after the FocusTrapZone is disabled. + +
+
-
* { + left: 0px; + position: relative; + top: 0px; + } + &:hover { + background-color: #eaeaea; + color: #212121; + } + @media screen and (-ms-high-contrast: active){&:hover { + border-color: Highlight; + color: Highlight; + } + &:active { + background-color: #c8c8c8; + color: #212121; + } + data-is-focusable={true} + onClick={[Function]} + onKeyDown={[Function]} + onKeyPress={[Function]} + onKeyUp={[Function]} + onMouseDown={[Function]} + onMouseUp={[Function]} + type="button" > -
- - + id="id__0" + > + Trap Focus +
+
-
+ +
+
+
* { + text-overflow: ellipsis; + } + & > *:not(:first-child) { + margin-top: 15px; + } + & > *:not(.ms-StackItem) { + flex-shrink: 1; } >
- +
+ + +
+
+
+
+ +
+ &:hover { + border-color: #333333; + } + @media screen and (-ms-high-contrast: active){&:hover { + border-color: Highlight; + } + > + +
+ + Hyperlink inside trap zone +
- - Hyperlink inside trap zone - +
`; diff --git a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.FocusOnCustomElement.Example.tsx.shot b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.FocusOnCustomElement.Example.tsx.shot index f45756455cb72..dca8f46c5f8f0 100644 --- a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.FocusOnCustomElement.Example.tsx.shot +++ b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Box.FocusOnCustomElement.Example.tsx.shot @@ -1,232 +1,249 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Component Examples renders FocusTrapZone.Box.FocusOnCustomElement.Example.tsx correctly 1`] = ` -
+
* { + text-overflow: ellipsis; + } + & > *:not(:first-child) { + margin-top: 8px; + } + & > *:not(.ms-StackItem) { + flex-shrink: 1; + } +>
* { - text-overflow: ellipsis; - } - & > *:not(:first-child) { - margin-top: 15px; - } - & > *:not(.ms-StackItem) { + > + + If this button is used to enable FocusTrapZone, the hyperlink should be focused. + +
+
-
* { + left: 0px; + position: relative; + top: 0px; + } + &:hover { + background-color: #eaeaea; + color: #212121; + } + @media screen and (-ms-high-contrast: active){&:hover { + border-color: Highlight; + color: Highlight; + } + &:active { + background-color: #c8c8c8; + color: #212121; + } + data-is-focusable={true} + onClick={[Function]} + onKeyDown={[Function]} + onKeyPress={[Function]} + onKeyUp={[Function]} + onMouseDown={[Function]} + onMouseUp={[Function]} + type="button" > -
- - + id="id__0" + > + Focus Custom Element +
+
-
+ +
+
+
* { + text-overflow: ellipsis; + } + & > *:not(:first-child) { + margin-top: 15px; + } + & > *:not(.ms-StackItem) { + flex-shrink: 1; } >
- +
+ + +
+
+
+
+ +
+ &:hover { + border-color: #333333; + } + @media screen and (-ms-high-contrast: active){&:hover { + border-color: Highlight; + } + > + +
+ + Hyperlink which will receive initial focus when trap zone is activated +
- - Hyperlink which will receive initial focus when trap zone is activated - +
`; diff --git a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.FocusZone.Example.tsx.shot b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.FocusZone.Example.tsx.shot index 3dde764483dd8..d678bddc49af6 100644 --- a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.FocusZone.Example.tsx.shot +++ b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.FocusZone.Example.tsx.shot @@ -1,7 +1,23 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Component Examples renders FocusTrapZone.FocusZone.Example.tsx correctly 1`] = ` -
+
+
+
`; diff --git a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Nested.Example.tsx.shot b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Nested.Example.tsx.shot index 33230fc326865..1ac9d20b0f13d 100644 --- a/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Nested.Example.tsx.shot +++ b/packages/office-ui-fabric-react/src/components/__snapshots__/FocusTrapZone.Nested.Example.tsx.shot @@ -118,323 +118,33 @@ exports[`Component Examples renders FocusTrapZone.Nested.Example.tsx correctly 1
* { - text-overflow: ellipsis; - } - & > *:not(:first-child) { - margin-top: 10px; - } - & > *:not(.ms-StackItem) { - flex-shrink: 1; - } + onBlur={[Function]} + onFocus={[Function]} + onFocusCapture={[Function]} >
- -
- - -
-
- + aria-hidden={true} + data-is-visible={true} + onFocus={[Function]} + style={ + Object { + "pointerEvents": "none", + "position": "fixed", + } + } + tabIndex={-1} + />
*:not(:first-child) { - margin-left: 10px; + margin-top: 10px; } & > *:not(.ms-StackItem) { flex-shrink: 1; @@ -462,7 +172,6 @@ exports[`Component Examples renders FocusTrapZone.Nested.Example.tsx correctly 1 font-size: 14px; font-weight: 400; margin-bottom: 8px; - width: 200px; } >
* { - text-overflow: ellipsis; - } - & > *:not(:first-child) { - margin-top: 10px; - } - & > *:not(.ms-StackItem) { - flex-shrink: 1; - } + onBlur={[Function]} + onFocus={[Function]} + onFocusCapture={[Function]} > +
* { + text-overflow: ellipsis; + } + & > *:not(:first-child) { + margin-top: 10px; + } + & > *:not(.ms-StackItem) { + flex-shrink: 1; } > - -
- -
-
- +
+ Off +
- -
-
* { - text-overflow: ellipsis; - } - & > *:not(:first-child) { - margin-top: 10px; - } - & > *:not(.ms-StackItem) { - flex-shrink: 1; - } - > -
- -
* { + left: 0px; position: relative; + top: 0px; + } + &:hover { + background-color: #eaeaea; + color: #212121; + } + @media screen and (-ms-high-contrast: active){&:hover { + border-color: Highlight; + color: Highlight; + } + &:active { + background-color: #c8c8c8; + color: #212121; } + data-is-focusable={true} + onClick={[Function]} + onKeyDown={[Function]} + onKeyPress={[Function]} + onKeyUp={[Function]} + onMouseDown={[Function]} + onMouseUp={[Function]} + type="button" > -
+ +
+
+
* { + text-overflow: ellipsis; } - .ms-Fabric--isFocusVisible &:focus:after { - border: 1px solid #ffffff; - bottom: -2px; - content: ""; - left: -2px; - outline: 1px solid #666666; - position: absolute; - right: -2px; - top: -2px; - z-index: 1; + & > *:not(:first-child) { + margin-top: 10px; } - &:hover { - border-color: #333333; + & > *:not(.ms-StackItem) { + flex-shrink: 1; } - @media screen and (-ms-high-contrast: active){&:hover .ms-Toggle-thumb { - border-color: Highlight; + > +
+ +
+ + +
+
+ +
+
+
+
+
+
* { + text-overflow: ellipsis; } - @media screen and (-ms-high-contrast: active){&:hover { - border-color: Highlight; + & > *:not(:first-child) { + margin-top: 10px; + } + & > *:not(.ms-StackItem) { + flex-shrink: 1; } - data-is-focusable={true} - id="Toggle15" - onChange={[Function]} - onClick={[Function]} - role="switch" - type="button" >
+ +
+ + +
+
+ + data-is-focusable={true} + onClick={[Function]} + onKeyDown={[Function]} + onKeyPress={[Function]} + onKeyUp={[Function]} + onMouseDown={[Function]} + onMouseUp={[Function]} + type="button" + > +
+
+
+ Zone 4 button +
+
+
+ +
+
+
+
+
+
+
+
+
* { + text-overflow: ellipsis; + } + & > *:not(:first-child) { + margin-top: 10px; + } + & > *:not(.ms-StackItem) { + flex-shrink: 1; + } + > +
-
-
- +
+ Off +
- -
-
-
* { - text-overflow: ellipsis; - } - & > *:not(:first-child) { - margin-top: 10px; - } - & > *:not(.ms-StackItem) { - flex-shrink: 1; - } - > -
- -
- -
-
-
- +
+
+
`;